Skip to content

Bug: HttpResolverLocal shares request state across concurrent ASGI requests #8451

Description

@wuodar

Expected behaviour

Concurrent requests to the same HttpResolver / HttpResolverLocal ASGI application should retain their own bodies, headers, and routing context. Invalid or cancelled requests should not clear another in-flight request's context.

Current behaviour

With native Pydantic validation enabled, six concurrent requests with distinct bodies all return the last request's body. This reproduces with 3.34.0 and current develop. An overlapping validation failure or cancellation can also clear a pending handler's context.

Reproduction

Install aws-lambda-powertools==3.34.0, pydantic, and httpx, then run:

import asyncio
import httpx
from pydantic import BaseModel
from aws_lambda_powertools.event_handler import HttpResolver

class Body(BaseModel):
    name: str

app = HttpResolver(enable_validation=True)

@app.post('/echo')
async def echo(body: Body) -> dict:
    await asyncio.sleep(0)
    return {'name': body.name}

async def main():
    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url='http://test') as client:
        responses = await asyncio.gather(*(
            client.post('/echo', json={'name': str(i)}) for i in range(6)
        ))
    names = [response.json()['name'] for response in responses]
    print(names)
    assert names == [str(i) for i in range(6)]

asyncio.run(main())

Expected: ['0', '1', '2', '3', '4', '5']

Observed: ['5', '5', '5', '5', '5', '5'], followed by the assertion failure.

Cause and proposed fix

HttpResolverLocal.asgi_handler() assigns BaseRouter.current_event and BaseRouter.lambda_context, then awaits resolution. Routing and validation also mutate a shared self.context dictionary. Validation middleware runs through a thread bridge, allowing another ASGI request to overwrite this state before the first consumes it. Request cleanup can clear another request's context.

A request-scoped state object carried by ContextVar can isolate local ASGI resolution, including middleware threads, without serializing all local requests. I am preparing a focused PR with regressions for distinct bodies, overlapping validation failures, and cancellation.

Scope

This concerns overlapping requests to the local ASGI server, not concurrent invocations within a Lambda execution environment. I understand the Lambda execution model; local ASGI servers nevertheless accept concurrent HTTP requests. This is separate from #8187 / #8196 (the validation-exception deadlock fixed in 3.29.0).

Environment

Python 3.13; Powertools 3.34.0 and current develop; local ASGI, no AWS resources required.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions