What
Both proxy event payloads omit most of their published members, the v1 event carries a member AWS does
not publish, and the PayloadFormatVersion a consumer configures is stored and never read.
1. PayloadFormatVersion is stored and ignored
apigatewayv2_types.go:66-87 decodes and persists PayloadFormatVersion on the integration. Nothing
reads it. The format is chosen instead by isV2 (apigateway_plugin.go:1605-1609), derived from
which API object the request arrived through. So an HTTP API integration explicitly configured with
PayloadFormatVersion: "1.0" — a supported, documented combination, and the one a consumer uses when
migrating a REST API handler to an HTTP API — receives a 2.0 event. The handler reads
event["httpMethod"], finds nothing, and fails on a configuration AWS supports.
2. The v1 event carries an unpublished version
:1778 emits "version": "1.0". The 1.0 payload format publishes no version member — it is a
2.0-only member. A handler that branches on "version" in event to tell the formats apart, which is
the idiomatic way to write a dual-format handler, takes the 2.0 branch for a 1.0 event.
3. The v1 event omits the multi-value members and most of requestContext
:1772-1806 emits 5 of the 15 published requestContext members, and omits
multiValueHeaders and multiValueQueryStringParameters entirely. Those two are how the 1.0 format
represents a repeated header or query parameter — ?tag=a&tag=b, or two Set-Cookies — so a handler
reading them reads nil and a repeated parameter is silently reduced to one value. Of the
requestContext members, the absent ones include identity (with sourceIp, userAgent),
authorizer, requestId, requestTime, protocol and resourcePath — identity.sourceIp being the
one most handlers actually use, for logging or rate limiting.
stageVariables is emitted as {} where AWS's own sample shows null.
4. The v2 event omits four published members
:1810-1842 omits cookies, queryStringParameters, pathParameters and stageVariables. The 2.0
format publishes all four. cookies is the notable one: in 2.0 it is the only place cookies
appear — they are not in headers — so a handler reading a session cookie from a 2.0 event gets
nothing at all.
Why this matters
A proxy integration's event payload is the interface between API Gateway and a consumer's handler,
and it is the one thing a handler's code reads directly on every request. Every other API Gateway
divergence affects whether a request arrives; these affect whether the handler can do its job once it
does. A handler that reads event["requestContext"]["identity"]["sourceIp"] — logging, rate limiting,
geo rules, audit — panics or reads nil against substrate and works against AWS.
The PayloadFormatVersion case is the sharpest because the consumer configured the thing that is
ignored. They wrote PayloadFormatVersion: "1.0" in their template, substrate stored it, and then sent
the other format. That is worse than an unimplemented member: it is a member whose configuration is
accepted and contradicted, so the consumer's reasonable conclusion is that their handler is broken.
The unpublished version member is the subtlest and the most likely to cost someone an afternoon,
because it makes the standard format-detection idiom give the wrong answer — and it gives it silently,
on the format where the mistake is hardest to see.
Acceptance criteria
Provenance
Both payload formats' complete member lists, the fifteen requestContext members, the samples showing
stageVariables: null in 1.0, and the statement that version is a 2.0 member are from
api-gateway-simple-proxy-for-lambda-input.html (1.0) and
http-api-develop-integrations-lambda.html (2.0). That cookies is the only cookie carrier in 2.0,
and that PayloadFormatVersion defaults to 2.0 and accepts 1.0, are from the same 2.0 page.
In-tree: emulator/apigatewayv2_types.go:66-87 (stored and unread);
emulator/apigateway_plugin.go:1605-1609 (isV2), :1778 (the unpublished version), :1772-1806
(the v1 event), :1810-1842 (the v2 event). Line citations are from the tree at the commit this issue
was filed against.
What
Both proxy event payloads omit most of their published members, the v1 event carries a member AWS does
not publish, and the
PayloadFormatVersiona consumer configures is stored and never read.1.
PayloadFormatVersionis stored and ignoredapigatewayv2_types.go:66-87decodes and persistsPayloadFormatVersionon the integration. Nothingreads it. The format is chosen instead by
isV2(apigateway_plugin.go:1605-1609), derived fromwhich API object the request arrived through. So an HTTP API integration explicitly configured with
PayloadFormatVersion: "1.0"— a supported, documented combination, and the one a consumer uses whenmigrating a REST API handler to an HTTP API — receives a 2.0 event. The handler reads
event["httpMethod"], finds nothing, and fails on a configuration AWS supports.2. The v1 event carries an unpublished
version:1778emits"version": "1.0". The 1.0 payload format publishes noversionmember — it is a2.0-only member. A handler that branches on
"version" in eventto tell the formats apart, which isthe idiomatic way to write a dual-format handler, takes the 2.0 branch for a 1.0 event.
3. The v1 event omits the multi-value members and most of
requestContext:1772-1806emits 5 of the 15 publishedrequestContextmembers, and omitsmultiValueHeadersandmultiValueQueryStringParametersentirely. Those two are how the 1.0 formatrepresents a repeated header or query parameter —
?tag=a&tag=b, or twoSet-Cookies — so a handlerreading them reads
niland a repeated parameter is silently reduced to one value. Of therequestContextmembers, the absent ones includeidentity(withsourceIp,userAgent),authorizer,requestId,requestTime,protocolandresourcePath—identity.sourceIpbeing theone most handlers actually use, for logging or rate limiting.
stageVariablesis emitted as{}where AWS's own sample showsnull.4. The v2 event omits four published members
:1810-1842omitscookies,queryStringParameters,pathParametersandstageVariables. The 2.0format publishes all four.
cookiesis the notable one: in 2.0 it is the only place cookiesappear — they are not in
headers— so a handler reading a session cookie from a 2.0 event getsnothing at all.
Why this matters
A proxy integration's event payload is the interface between API Gateway and a consumer's handler,
and it is the one thing a handler's code reads directly on every request. Every other API Gateway
divergence affects whether a request arrives; these affect whether the handler can do its job once it
does. A handler that reads
event["requestContext"]["identity"]["sourceIp"]— logging, rate limiting,geo rules, audit — panics or reads nil against substrate and works against AWS.
The
PayloadFormatVersioncase is the sharpest because the consumer configured the thing that isignored. They wrote
PayloadFormatVersion: "1.0"in their template, substrate stored it, and then sentthe other format. That is worse than an unimplemented member: it is a member whose configuration is
accepted and contradicted, so the consumer's reasonable conclusion is that their handler is broken.
The unpublished
versionmember is the subtlest and the most likely to cost someone an afternoon,because it makes the standard format-detection idiom give the wrong answer — and it gives it silently,
on the format where the mistake is hardest to see.
Acceptance criteria
PayloadFormatVersionselects the event format. A1.0integration on an HTTP API receives a1.0 event; the default when unset is
2.0for an HTTP API, as AWS documents.version.multiValueHeadersandmultiValueQueryStringParameters, so a repeatedheader or query parameter survives.
requestContextcarries its published members,identity.sourceIpandrequestIdincluded.
stageVariablesmatches the published sample —nullwhen there are none, in v1.cookies,queryStringParameters,pathParametersandstageVariables.iam_shape_members_test.go:115. Substrate's own tests build the event and read it back throughits own structs, which is why ten missing members survived.
?tag=a&tag=band two cookies through both formats and asserts both values survivein the format-appropriate member.
PayloadFormatVersion: "1.0"on an HTTP API and asserts a 1.0 event. It mustfail against today's tree.
docs/services.md'sexecute-apisection carries both event shapes, stating which memberssubstrate emits.
Provenance
Both payload formats' complete member lists, the fifteen
requestContextmembers, the samples showingstageVariables: nullin 1.0, and the statement thatversionis a 2.0 member are fromapi-gateway-simple-proxy-for-lambda-input.html(1.0) andhttp-api-develop-integrations-lambda.html(2.0). Thatcookiesis the only cookie carrier in 2.0,and that
PayloadFormatVersiondefaults to2.0and accepts1.0, are from the same 2.0 page.In-tree:
emulator/apigatewayv2_types.go:66-87(stored and unread);emulator/apigateway_plugin.go:1605-1609(isV2),:1778(the unpublishedversion),:1772-1806(the v1 event),
:1810-1842(the v2 event). Line citations are from the tree at the commit this issuewas filed against.