Expected Behaviour
I'm trying to update to a newer version of aws-lambda-powertools, and I'm hitting problems for endpoints that return "empty" 204 responses (e.g. for a DELETE endpoint). For example:
def delete_endpoint() -> Response[None]:
return Response(
status_code=204,
body=None,
)
seems to follow the recommendations for the "Using Response with data validation?" section in the docs here.
I would expect both ALBResolver and APIGatewayHttpResolver to treat this the same, and they agree in aws-lambda-powertools==3.25.0 but not in aws-lambda-powertools==3.26.0 and higher.
My suspicion is that this modification of the response body is the cause:
|
# NOTE: Minor override for early return on Response with null body for ALB |
|
if isinstance(result, Response) and result.body is None: |
|
logger.debug("ALB doesn't allow None responses; converting to empty string") |
|
result.body = "" |
Current Behaviour
The ALBResolver returns a response like
{
"statusCode": 422,
"body": "{\"statusCode\":422,\"detail\":[{\"loc\":[\"response\"],\"type\":\"none_required\"}]}",
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
}
}
Code snippet
# /// script
# dependencies = [
# "pydantic==2.12.0",
# "aws-lambda-powertools==3.28.0",
# ]
# ///
import json
from typing import Any
from aws_lambda_powertools.event_handler import ALBResolver, APIGatewayHttpResolver, Response
def build_alb_event(*, path: str, method: str) -> dict[str, Any]:
event: dict[str, Any] = {
"httpMethod": method,
"path": path,
"requestId": "some_request_id",
"requestContext": {"elb": {"targetGroupArn": ":target:"}},
}
return event
def build_api_gateway_event(*, path: str, method: str) -> dict[str, Any]:
event: dict[str, Any] = {
"rawPath": path,
"requestContext": {
"requestContext": {"requestId": "some_request_id"},
"http": {"method": method},
"stage": "$default",
},
}
return event
alb_app = ALBResolver(enable_validation=True)
api_gateway_app = APIGatewayHttpResolver(enable_validation=True)
# I tried multilple return types for the endpoint
# - No return type: OK
# - typing.Any: OK
# - None: 422 from ALB
# - Response[None]: 422 from ALB
# - Response[str]: 422 from API Gateway
# - Response[str] with body="": OK
def delete_endpoint() -> Response[None]:
return Response(
status_code=204,
body=None,
)
alb_app.route("test", "DELETE")(delete_endpoint)
api_gateway_app.route("test", "DELETE")(delete_endpoint)
alb_resp = alb_app.resolve( build_alb_event(path="test", method="DELETE"), context={} )
api_resp = api_gateway_app.resolve( build_api_gateway_event(path="test", method="DELETE"), context={})
print(f"alb: {json.dumps(alb_resp, indent=2)}")
print(f"api: {json.dumps(api_resp, indent=2)}")
Possible Solution
As noted in the snippet, I tried a few different return type annotations on the function. Omitting the return type, or using typing.Any seems to fix (or at least hide) the problem. The only fix that maintains typing while fixing both ALB and APIGateway behavior seems to returning an empty string instead of None for the Response body, and using Response[str] as the type annotation.
Steps to Reproduce
Run the attached script. I used uv run repro_204.py, but installing with pip should also work.
Powertools for AWS Lambda (Python) version
3.26.0 and higher
AWS Lambda function runtime
3.12
Packaging format used
Lambda Layers
Debugging logs
Expected Behaviour
I'm trying to update to a newer version of
aws-lambda-powertools, and I'm hitting problems for endpoints that return "empty" 204 responses (e.g. for a DELETE endpoint). For example:seems to follow the recommendations for the "Using Response with data validation?" section in the docs here.
I would expect both ALBResolver and APIGatewayHttpResolver to treat this the same, and they agree in
aws-lambda-powertools==3.25.0but not inaws-lambda-powertools==3.26.0and higher.My suspicion is that this modification of the response body is the cause:
powertools-lambda-python/aws_lambda_powertools/event_handler/api_gateway.py
Lines 3361 to 3364 in 08c9921
Current Behaviour
The ALBResolver returns a response like
{ "statusCode": 422, "body": "{\"statusCode\":422,\"detail\":[{\"loc\":[\"response\"],\"type\":\"none_required\"}]}", "isBase64Encoded": false, "headers": { "Content-Type": "application/json" } }Code snippet
Possible Solution
As noted in the snippet, I tried a few different return type annotations on the function. Omitting the return type, or using
typing.Anyseems to fix (or at least hide) the problem. The only fix that maintains typing while fixing both ALB and APIGateway behavior seems to returning an empty string instead ofNonefor theResponsebody, and usingResponse[str]as the type annotation.Steps to Reproduce
Run the attached script. I used
uv run repro_204.py, but installing withpipshould also work.Powertools for AWS Lambda (Python) version
3.26.0 and higher
AWS Lambda function runtime
3.12
Packaging format used
Lambda Layers
Debugging logs