From b1e9eed8a98e913e5b2d617e7abba36e0be7d908 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Sat, 2 May 2026 16:16:06 +0100 Subject: [PATCH 1/2] fix(event_handler): handle ALB response when it's None --- .../event_handler/api_gateway.py | 11 +---------- .../middlewares/openapi_validation.py | 6 +++++- .../test_openapi_validation_middleware.py | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index d5d7751f043..a323cf67d56 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -3343,22 +3343,13 @@ def _get_base_path(self) -> str: # ALB doesn't have a stage variable, so we just return an empty string return "" - # BedrockResponse is not used here but adding the same signature to keep strong typing @override def _to_response(self, result: dict | tuple | Response | BedrockResponse) -> Response | BedrockResponse: """Convert the route's result to a Response ALB requires a non-null body otherwise it converts as HTTP 5xx - - 3 main result types are supported: - - - Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to - application/json - - Tuple[dict, int]: Same dict handling as above but with the option of including a status code - - Response: returned as is, and allows for more flexibility """ - - # NOTE: Minor override for early return on Response with null body for ALB + # ALB doesn't support null body - convert before building the final response if isinstance(result, Response) and result.body is None: logger.debug("ALB doesn't allow None responses; converting to empty string") result.body = "" diff --git a/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py b/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py index 05306b5ca8b..4699d213808 100644 --- a/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py +++ b/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py @@ -296,9 +296,13 @@ def _handle_response(self, *, route: Route, response: Response): # JSON serialize the body without validation response.body = jsonable_encoder(response.body, custom_serializer=self._validation_serializer) else: + # ALB resolver converts None body to "" to prevent ALB 5xx errors, + # but the validation should still see it as None. + response_content = None if response.body == "" and field.type_ is None else response.body + response.body = self._serialize_response_with_validation( field=field, - response_content=response.body, + response_content=response_content, has_route_custom_response_validation=route.custom_response_validation_http_code is not None, ) diff --git a/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py b/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py index e7199adc9c5..7c0ccd41c72 100644 --- a/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py +++ b/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py @@ -4227,3 +4227,21 @@ def handler(session_id: Annotated[str, Cookie()]): assert result["statusCode"] == 200 body = json.loads(result["body"]) assert body["session_id"] == "lattice_v1_abc" + + +def test_alb_response_none_body_with_validation(gw_event_alb): + # GIVEN an ALBResolver with validation enabled + app = ALBResolver(enable_validation=True) + + gw_event_alb["path"] = "/no-content" + gw_event_alb["httpMethod"] = "DELETE" + + # WHEN a handler returns Response with body=None and return type is None + @app.delete("/no-content") + def handler() -> None: + return Response(status_code=204, body=None) + + # THEN the response should be 204 with empty body (not 422 validation error) + result = app(gw_event_alb, {}) + assert result["statusCode"] == 204 + assert result["body"] == "" From 2e60f636e2e2b25ce0edb103e91f5a42e5435241 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Sun, 3 May 2026 12:01:06 +0100 Subject: [PATCH 2/2] fix(event_handler): handle ALB response when it's None --- .../middlewares/openapi_validation.py | 2 +- .../test_openapi_validation_middleware.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py b/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py index 4699d213808..470a19e6c54 100644 --- a/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py +++ b/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py @@ -298,7 +298,7 @@ def _handle_response(self, *, route: Route, response: Response): else: # ALB resolver converts None body to "" to prevent ALB 5xx errors, # but the validation should still see it as None. - response_content = None if response.body == "" and field.type_ is None else response.body + response_content = None if response.body == "" and field.type_ in (None, type(None)) else response.body response.body = self._serialize_response_with_validation( field=field, diff --git a/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py b/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py index 7c0ccd41c72..0da092f8aea 100644 --- a/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py +++ b/tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py @@ -4245,3 +4245,21 @@ def handler() -> None: result = app(gw_event_alb, {}) assert result["statusCode"] == 204 assert result["body"] == "" + + +def test_alb_response_typed_none_body_with_validation(gw_event_alb): + # GIVEN an ALBResolver with validation enabled + app = ALBResolver(enable_validation=True) + + gw_event_alb["path"] = "/no-content" + gw_event_alb["httpMethod"] = "DELETE" + + # WHEN a handler returns Response[None] with body=None + @app.delete("/no-content") + def handler() -> Response[None]: + return Response(status_code=204, body=None) + + # THEN the response should be 204 with empty body (not 422 validation error) + result = app(gw_event_alb, {}) + assert result["statusCode"] == 204 + assert result["body"] == ""