Skip to content

fix(event_handler): unify header validation, scalar list joining, and content-type parsing - #8602

Open
yagruda wants to merge 2 commits into
aws-powertools:developfrom
yagruda:fix-openapi-header-validation
Open

yagruda wants to merge 2 commits into
aws-powertools:developfrom
yagruda:fix-openapi-header-validation

Conversation

@yagruda

@yagruda yagruda commented Oct 18, 2026

Copy link
Copy Markdown

Issue number: closes #8581

Summary

Changes

  • In OpenAPIRequestValidationMiddleware:
    • Added sequence parameter handling in _normalize_multi_params and _process_model_param to convert single string values into single-item lists (and split comma strings if unparsed), ensuring sequence headers like list[str] validate consistently across HTTP API, Function URL, ALB, and VPC Lattice.
    • Allowed scalar header parameters to be joined via _restore_scalar_parameter by removing the multi-value exclusion for headers, aligning with RFC 9110 Section 5.3 where multiple header lines are equivalent to a comma-separated list.
    • Updated _get_body to extract the media type without parameters, compare case-insensitively, and accept +json media types (such as application/problem+json and application/vnd.api+json).
  • Updated test_native_repeated_parameters_keep_existing_behavior in test_openapi_comma_parameters.py and added regression test cases in test_openapi_validation_middleware.py.

User experience

  • Declaring a route header like x_country: Annotated[list[str], Header(alias="x-country")] accepts single values on all resolvers (e.g. x-country: US) instead of failing with 422 list_type on HTTP API / Function URL / ALB.
  • Repeated header lines for a scalar string parameter (e.g. in multiValueHeaders) are joined into a comma-delimited string consistently across REST and HTTP API resolvers instead of raising 422 string_type.
  • Valid requests with Content-Type: Application/JSON or application/problem+json are parsed as JSON rather than rejected with 415.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@yagruda
yagruda requested a review from a team as a code owner October 18, 2026 11:24
@yagruda
yagruda requested a review from svozza October 18, 2026 11:24
@boring-cyborg

boring-cyborg Bot commented Oct 18, 2026

Copy link
Copy Markdown

Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @yagruda, confirmed on all three points. I ran it behind a real REST API and a real HTTP API with the same function: a single x-country value is accepted as a one-item list on every resolver, repeated x-req lines on REST come back as "a,b" like HTTP API already did, and Application/JSON, application/problem+json and application/vnd.api+json are parsed as JSON while text/plain still gets a 415. Comparing the media type exactly also closed a small hole, application/jsonx used to pass the startswith check.

Approving. This goes out in 3.39.0, so it will sit here for a while before I merge it. No rebase needed in the meantime, and you'll be credited in the release notes. I'll mention the repeated-header change on REST in the notes since it turns a 422 into a 200.

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more push from my side: the two [value] branches were unreachable, headers always arrive as a string and query strings as a list, so I removed them and added tests for the paths that were still uncovered: an optional sequence header that is not sent, and a header model whose list[str] field gets a single value or a comma-joined one. Patch coverage is 100% now. Re-approving, nothing changes for you.

@sonarqubecloud

Copy link
Copy Markdown

@yagruda

yagruda commented Oct 25, 2026

Copy link
Copy Markdown
Author

One more push from my side: the two [value] branches were unreachable, headers always arrive as a string and query strings as a list, so I removed them and added tests for the paths that were still uncovered: an optional sequence header that is not sent, and a header model whose list[str] field gets a single value or a comma-joined one. Patch coverage is 100% now. Re-approving, nothing changes for you.

Nice job, thank you!

This branch has not been deployed

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

Labels

event_handlers size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: header validation is inconsistent across resolvers (single-value list, repeated headers, Content-Type casing)

2 participants