Repository navigation
Bug: AppSync Events swallows UnauthorizedException in non-aggregate handlers, class-based async middleware leaks a coroutine, HttpResolverLocal is not usable as a Lambda handler #8582
Copy link
Copy link
Labels
bugSomething isn't workingSomething isn't workingevent_handlersgood first issueGood for newcomersGood for newcomerstriagePending triage from maintainersPending triage from maintainers
Milestone
Description
Expected Behaviour
- AppSync Events: raising
UnauthorizedExceptionin a publish handler fails the invocation so the publisher gets 401/403, as the docs say, regardless ofaggregate. - A class-based middleware with
async def handlerworks inresolve_async(), as the docs say. HttpResolverLocalworks as a Lambda handler (handler = app) and accepts binary request bodies.
Current Behaviour
- AppSync Events swallows
UnauthorizedExceptionin non-aggregate handlers (events_appsync/appsync_events.py:257sync,:316async withgather(return_exceptions=True)). The documented exampleworking_with_authorization_control.pyuses the defaultaggregate=False. Against a real AppSync Events API:Events are not delivered in either case, so it is not a bypass, but the status is wrong and the internal exception message reaches the publisher.aggregate=False: HTTP 200 {"failed":[{"code":"CustomError","message":"UnauthorizedException - not allowed"}, ...],"successful":[]} aggregate=True: HTTP 401 {"errors":[{"errorType":"UnauthorizedException","message":"Not authorized to access channel agg"}]} - Class-based async middleware leaks a coroutine (
middlewares/async_utils.py:158). The__call__wrapper is sync, soiscoroutinefunctionis False, it runs in a thread and the returned coroutine is treated as the body:TypeError: Object of type coroutine is not JSON serializable. Alsowrap_middleware_async(line 101) raisesIndexErrorwhen a sync middleware short-circuits. HttpResolverLocalas Lambda handler (http_resolver.py:348, 270, 72).handler = appfrom the docs example raisesTypeError(__call__is ASGI only);app.resolve()with an HTTP API or Function URL event raisesKeyError: 'httpMethod'; a binary body through ASGI raisesUnicodeDecodeError.
Code snippet
import asyncio
from aws_lambda_powertools.event_handler import APIGatewayRestResolver, AppSyncEventsResolver, HttpResolver
from aws_lambda_powertools.event_handler.events_appsync.exceptions import UnauthorizedException
from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler
from tests.functional.utils import load_event
events = AppSyncEventsResolver()
@events.on_publish("/default/*")
def deny(payload): raise UnauthorizedException("nope")
print(events.resolve(load_event("appSyncEventsEvent.json"), None))
# {"events": [{"id": "1", "error": "UnauthorizedException - nope"}, ...]} (expected: exception propagates)
class Mw(BaseMiddlewareHandler):
async def handler(self, app, nxt): return await nxt(app)
app = APIGatewayRestResolver()
@app.get("/my/path", middlewares=[Mw()])
async def h(): return {"ok": True}
asyncio.run(app.resolve_async(load_event("apiGatewayProxyEvent.json"), None))
# TypeError: Object of type coroutine is not JSON serializable
local = HttpResolver()
@local.get("/my/path")
def p(): return {"ok": True}
local(load_event("apiGatewayProxyEvent.json"), None) # TypeError: missing 'send'
local.resolve(load_event("apiGatewayProxyV2Event.json"), None) # KeyError: 'httpMethod'Possible Solution
Re-raise UnauthorizedException before the generic except in the per-event loop and after gather in the async path. In AsyncMiddlewareFrame.__call__, detect iscoroutinefunction(getattr(mw, "handler", None)) and await coroutine results; return the held result in wrap_middleware_async. Make HttpResolverLocal.__call__ dispatch on scope["type"] vs (event, context), fall back to the parent event mapping, and base64-encode non-UTF-8 bodies.
Steps to Reproduce
Run the snippet above with the tests/events fixtures. Full context in #8576.
Powertools for AWS Lambda (Python) version
latest
AWS Lambda function runtime
3.13
Packaging format used
PyPi
Debugging logs
Warning
This issue is ready to be picked up, by humans or by agents. If you want it, comment here first so we can assign it to you, and please take only one or two issues at a time so others can join in too. Thanks!
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingevent_handlersgood first issueGood for newcomersGood for newcomerstriagePending triage from maintainersPending triage from maintainers
Type
Projects
- StatusShow more project fieldsTriage
Activity
jasperdingg commentedon Oct 18, 2026
I'd love to give this a try! Will open a PR soon.
SuellenQwQ commentedon Oct 19, 2026
Items 1 and 2 are covered in #8633:
UnauthorizedExceptionraised by a per-event (aggregate=False) resolver now fails the whole request, in both the sync and the async path.BaseMiddlewareHandlerwithasync def handler) are now awaited byAsyncMiddlewareFrame, andwrap_middleware_asyncno longer raisesIndexErrorwhen a middleware short-circuits.Item 3 (
HttpResolverLocalas a Lambda handler) is untouched: it needs a dispatch change in__call__for the ASGI/Lambda mix, so I left it for a follow-up.fix(event_handler): make HttpResolverLocal usable as a Lambda handler
fix(event_handler): make HttpResolverLocal usable as a Lambda handler
jasperdingg commentedon Oct 19, 2026
#8633 covers items 1 and 2, so I opened #8634 for item 3! Happy to adjust the approach if needed.
leandrodamascena commentedon Oct 19, 2026
Hey @SuellenQwQ and @jasperdingg, thanks for sorting the split between you. @SuellenQwQ items 1 and 2 in #8633, @jasperdingg item 3 in #8634. I'll review both.