Closed
Description
Expected Behaviour
When using quoted type annotations with Annotated in AWS Lambda Powertools API Gateway resolver, the parameter should be correctly identified as a body parameter and properly parse the incoming JSON request body.
When using quoted type annotations (forward references) like 'Annotated[UserCreate, Body(...)]', the parameter is incorrectly treated as a query parameter instead of a body parameter. This results in a 422 Unprocessable Content error with a "missing query parameter" message, even when the JSON body is correctly provided.
Current Behaviour
- Type resolution fails silently
- Parameter placement falls back to "best guess" behavior
- Complex types may be misclassified as simple types
- Explicit parameter annotations are ignored
Code snippet
import json
from typing import Annotated
from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.event_handler.openapi.params import Body
from pydantic import BaseModel
app = APIGatewayRestResolver(enable_validation=True)
class UserCreate(BaseModel):
username: str
email: str
age: int
id: str
@app.post("/users")
def create_user(
user_create_request: 'Annotated[UserCreate, Body(title="New User", description="The user data to create")]',
) -> dict:
return {"message": f"Created user {user_create_request.username}"}
def lambda_handler(event, context):
return app.resolve(event, context)
if __name__ == "__main__":
test_event = {
"body": json.dumps({
"username": "john_doe",
"email": "[email protected]",
"age": 30,
"id": "user123"
}),
"httpMethod": "POST",
"path": "/users",
"headers": {
"Content-Type": "application/json"
},
"requestContext": {
"requestId": "test-id"
}
}
result = lambda_handler(test_event, {})
if (
result["statusCode"] == 422
and "missing" in result["body"]
and "user_create_request" in result["body"]
):
print("Bug reproduced! Got expected 422 error with missing user_create_request:")
print(result["body"])
else:
print("Unexpected result:")
print(f"Status: {result['statusCode']}")
print(f"Body: {result['body']}")
"""
The issue occurs due to how AWS Lambda Powertools handles type resolution:
1. In get_typed_signature(), there's a bug in how it gets the globals for the call:
globalns = getattr(call, "__global__", {}) # Bug: Returns empty dict if __global__ not found
and that is always true becuase it's not __global_ its __globals__
This fails to get the actual module globals where UserCreate is defined.
2. When evaluating the forward reference string in get_typed_annotation():
annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns)
Because globalns is empty, it can't resolve "UserCreate" or other types in the string.
3. This causes the unresolved ForwardRef to be passed to field_annotation_is_complex(),
where get_origin() returns None since it can't get the origin of an unresolved reference.
4. With origin=None, field_annotation_is_complex() returns False, making
field_annotation_is_scalar() return True.
5. Because the parameter is incorrectly identified as a scalar field,
is_body_param() returns False and the parameter isn't added to
dependant.body_params.
6. This results in the parameter being treated as a query parameter
instead of a body parameter, leading to the missing argument error.
The fix could be either:
a) Use direct type annotation instead of a string forward reference
b) Fix get_typed_signature() to properly get the module globals:
globalns = getattr(call, "__globals__", {}) # Use current globals as fallback
"""
Possible Solution
Fix get_typed_signature() to properly get the module globals:
globalns = getattr(call, "__globals__", {})
Steps to Reproduce
Steps to Reproduce
- Create an API Gateway resolver with validation enabled
- Define a Pydantic model for request validation
- Create an endpoint using a quoted type annotation with
Annotated
andBody
- Send a POST request with a valid JSON body
- Observe the 422 error indicating a missing query parameter
Powertools for AWS Lambda (Python) version
latest
AWS Lambda function runtime
3.8
Packaging format used
Lambda Layers
Debugging logs
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Shipped