Skip to content

fix(event_handler): keep catch-all exception handlers off 404s and adapt exception handler returns - #8624

Merged
leandrodamascena merged 2 commits into
aws-powertools:developfrom
MrNK2107:fix/exception-handler-not-found-and-to-response
Oct 22, 2026
Merged

leandrodamascena merged 2 commits into
aws-powertools:developfrom
MrNK2107:fix/exception-handler-not-found-and-to-response

Conversation

@MrNK2107

@MrNK2107 MrNK2107 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Issue number: closes #8579

Summary

Changes

  1. 404 hijacked by catch-all handlers. not_found_handler (sync and async) used lookup_exception_handler(NotFoundError), which walks the MRO up to Exception. A new _lookup_not_found_handler() walks the same MRO but stops before Exception/BaseException. Handlers for NotFoundError (@app.not_found) and ServiceError still format 404s as before. Only catch-all handlers are skipped, since no exception was actually raised.
  2. body: null on ALB, and dict/tuple returns crashing. _call_exception_handler now passes the handler's return value through _to_response, the same adaptation a route's return value gets. As a result, ALBResolver._to_response turns body=None into "", and {...} / ({...}, status) are serialized as JSON.
    • The ALB response-size path already did this by hand after calling _call_exception_handler, so I removed that now-redundant line.

New tests:

  • test_api_gateway.py: a catch-all handler no longer replaces the 404, a ServiceError handler still formats 404s, a None body on ALB becomes "", and dict and tuple returns are serialized.
  • test_resolve_async.py: a catch-all handler no longer replaces the 404 on the async path.

All event_handler tests pass. Locally on Windows, test_openapi_encode_purepath and test_load_resolver_file_not_found also fail on an unmodified develop, because of path separators.

User experience

Before:

@app.exception_handler(Exception)
def generic(ex): return Response(500, "text/plain", "oops")
# GET /nope -> 500 "oops"

@app.exception_handler(ValueError)
def bad(ex): return {"error": str(ex)}, 400
# -> AttributeError: 'tuple' object has no attribute 'is_json'

After: GET /nope returns the default 404, and the tuple becomes a 400 JSON response. On ALBResolver, a handler returning Response(status_code=400, body=None) sends "body": "" instead of null.


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.

@MrNK2107
MrNK2107 requested a review from a team as a code owner October 18, 2026 19:09
@MrNK2107
MrNK2107 requested a review from svozza October 18, 2026 19:09
@boring-cyborg

boring-cyborg Bot commented Oct 18, 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 18, 2026
…ion handler returns

- An unmatched route no longer resolves to a handler registered for Exception
  or BaseException. No exception was raised, so a catch-all 500 handler (as the
  docs suggest) turned every unmatched route into a 500. Handlers for
  NotFoundError and ServiceError still apply.
- Whatever an exception handler returns now goes through _to_response, like a
  route's return value: Response(body=None) on ALB becomes "" instead of null
  (ALB 502), and dict / (dict, status) returns are serialized instead of raising
  AttributeError.

Fixes aws-powertools#8579
@MrNK2107
MrNK2107 force-pushed the fix/exception-handler-not-found-and-to-response branch from 6cd0a9d to 54e7e6f Compare October 18, 2026 19:10
@leandrodamascena leandrodamascena added this to the 3.38.0 milestone Oct 19, 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 @MrNK2107! I ran the suites and deployed this behind a real REST API: an unmatched route goes back to the default 404 even with a catch-all handler registered, a handler returning a tuple gives a proper 400 JSON instead of the AttributeError, and real exceptions still reach the catch-all. I also checked @app.not_found, a ServiceError handler, the [Exception, NotFoundError] list, the ALB None body, the Depends path and Bedrock, all behave. Nice touch dropping the now redundant _to_response in the ALB size path.

Approving now. This goes out in 3.38.0, so it will sit here a bit before I merge. No need to rebase or touch anything, I'll take care of it and you'll get the credit in the release notes. Thank you!

@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.93%. Comparing base (9c6ca41) to head (5817f68).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
aws_lambda_powertools/event_handler/api_gateway.py 90.90% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #8624      +/-   ##
===========================================
- Coverage    96.94%   96.93%   -0.01%     
===========================================
  Files          318      318              
  Lines        16051    16058       +7     
  Branches      1427     1430       +3     
===========================================
+ Hits         15560    15566       +6     
  Misses         349      349              
- Partials       142      143       +1     

☔ 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

@leandrodamascena
leandrodamascena merged commit 781f60e into aws-powertools:develop Oct 22, 2026
12 of 14 checks passed
@boring-cyborg

boring-cyborg Bot commented Oct 22, 2026

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

@powertools-for-aws-oss-automation

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

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: exception handlers hijack the default 404 and bypass _to_response (body null on ALB, dict/tuple crash)

2 participants