Skip to content

fix(event_handler): handle exceptions raised in Depends() like route exceptions - #8568

Merged
leandrodamascena merged 2 commits into
aws-powertools:developfrom
AtulJoshi1206:fix/event-handler-depends-exceptions
Oct 18, 2026
Merged

leandrodamascena merged 2 commits into
aws-powertools:developfrom
AtulJoshi1206:fix/event-handler-depends-exceptions

Conversation

@AtulJoshi1206

Copy link
Copy Markdown
Contributor

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 in DependencyResolutionError, which only subclasses Exception. _call_exception_handler() looked only at the wrapper, so three things never applied:

  • handlers registered for the original exception;
  • the built-in ServiceError responses;
  • the validation error responses.

The wrapper then escaped resolve().

What this PR does:

  • Handles the cause. ApiGatewayResolver._call_exception_handler(), the single dispatch point for resolve() and resolve_async(), now handles the wrapper's __cause__, unless the app registered a handler for DependencyResolutionError itself.

  • Keeps the fallback unchanged. If nothing handles the original exception, the caller still re-raises the original DependencyResolutionError unchanged: same object, traceback and __cause__. The existing test_depends_exception_raises_dependency_resolution_error contract 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 support Depends().

  • Tests, in tests/functional/event_handler/required_dependencies/test_depends.py:

    • a ServiceError returns its status code and body;
    • a custom exception_handler runs;
    • a nested dependency;
    • an explicit DependencyResolutionError handler still wins;
    • resolve_async(), handled and unhandled;
    • a parametrized 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 check and ruff format --check: clean.
  • mypy: no new errors.

User experience

Before: with the docs example dependency_injection_with_request.py, a request without x-user-id makes app.resolve() raise DependencyResolutionError. The invocation fails and the client gets a 5xx instead of a 401, and @app.exception_handler(MyError) never runs for a MyError raised 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

  • Catch-all handlers: a catch-all @app.exception_handler(Exception) now receives the original exception (e.g. UnauthorizedError) instead of DependencyResolutionError, the same as for route errors.
  • Workaround handlers: apps that registered a DependencyResolutionError handler 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

…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.
@AtulJoshi1206
AtulJoshi1206 requested a review from a team as a code owner October 17, 2026 05:14
@AtulJoshi1206
AtulJoshi1206 requested a review from hjgraca October 17, 2026 05:14
@boring-cyborg

boring-cyborg Bot commented Oct 17, 2026

Copy link
Copy Markdown

Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Oct 17, 2026

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

codecov Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.93%. Comparing base (93e577c) to head (92d5c8d).
⚠️ Report is 1 commits behind head on develop.

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

event_handlers size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: exceptions raised in a Depends() dependency bypass exception handlers (5xx instead of 401)

2 participants