Repository navigation
fix(event_handler): handle exceptions raised in Depends() like route exceptions - #8568
Conversation
…exceptions solve_dependencies() wraps anything a dependency raises in a DependencyResolutionError, which subclasses only Exception, and _call_exception_handler() only looks at the wrapper. Handlers registered for the original exception and the built-in ServiceError and validation responses never apply. An UnauthorizedError raised in a dependency, as in the docs example, fails the invocation and the client gets a 5xx instead of a 401. Handle the original exception as if the route had raised it, unless a handler is registered for DependencyResolutionError itself. If nothing handles it, the DependencyResolutionError is re-raised unchanged.
|
Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need. |
leandrodamascena
left a comment
There was a problem hiding this comment.
Thanks @AtulJoshi1206! I reproduced it on develop with the docs example and confirmed the 401 with this fix. Also checked nested dependencies, enable_validation=True, resolve_async and the catch-all handler, all behave like a route raising the same exception. And thanks for so many PRs improving Powertools lately, really appreciate it. Approving.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #8568 +/- ##
========================================
Coverage 96.93% 96.93%
========================================
Files 318 318
Lines 16002 16005 +3
Branches 1419 1420 +1
========================================
+ Hits 15511 15514 +3
Misses 349 349
Partials 142 142 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|



Issue number: closes #8567
Summary
Changes
Exceptions raised by a
Depends()dependency are now handled exactly as if the route had raised them.solve_dependencies()wraps anything a dependency raises inDependencyResolutionError, which only subclassesException._call_exception_handler()looked only at the wrapper, so three things never applied:ServiceErrorresponses;The wrapper then escaped
resolve().What this PR does:
Handles the cause.
ApiGatewayResolver._call_exception_handler(), the single dispatch point forresolve()andresolve_async(), now handles the wrapper's__cause__, unless the app registered a handler forDependencyResolutionErroritself.Keeps the fallback unchanged. If nothing handles the original exception, the caller still re-raises the original
DependencyResolutionErrorunchanged: same object, traceback and__cause__. The existingtest_depends_exception_raises_dependency_resolution_errorcontract and the debug-mode 500 are unchanged.Scope. This covers every proxy resolver (REST, HTTP API, ALB, Function URL, VPC Lattice, Bedrock Agent) and
HttpResolverLocal, since they all dispatch through this method. AppSync and the Bedrock function resolver don't supportDepends().Tests, in
tests/functional/event_handler/required_dependencies/test_depends.py:ServiceErrorreturns its status code and body;exception_handlerruns;DependencyResolutionErrorhandler still wins;resolve_async(), handled and unhandled;app.resolve()test over the REST, HTTP API, ALB and VPC Lattice v2 fixtures.The 8 bug tests fail on
develop, and the 2 controls pass on both.Checks:
pytest tests/functional/event_handler tests/unit/event_handler: 1171 passed, 1 skipped.ruff checkandruff format --check: clean.mypy: no new errors.User experience
Before: with the docs example
dependency_injection_with_request.py, a request withoutx-user-idmakesapp.resolve()raiseDependencyResolutionError. The invocation fails and the client gets a 5xx instead of a 401, and@app.exception_handler(MyError)never runs for aMyErrorraised in a dependency.After: the same request returns
401 {"statusCode": 401, "message": "Missing authentication"}, and exception handlers run for exceptions raised in dependencies, the same as for route errors.Risks
@app.exception_handler(Exception)now receives the original exception (e.g.UnauthorizedError) instead ofDependencyResolutionError, the same as for route errors.DependencyResolutionErrorhandler as a workaround are unaffected. It still receives the wrapper, with the original exception as__cause__.AI assistance: prepared with Claude Code; the change was reviewed and the tests were run locally.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.
🤖 Generated with Claude Code