Closed as not planned
Closed as not planned
Description
Expected Behavior
-
The X-Amzn-Trace-Id header is added once per outgoing request.
-
On retries, the instrumentation should either replace the trace header or prevent duplicate appends so that the header does not grow over successive invocations.
Current Behavior
- When a Lambda function triggered by SQS (with DLQ enabled) fails and is retried, the Powertools middleware re-applies the X-Amzn-Trace-Id header.
- This causes the header to accumulate extra trace context (additional Parent and Sampled values) on each retry.
- Eventually, the header becomes so large that nginx returns a 431 "Request Header Fields Too Large" error.
Code snippet
public instrumentFetch(): void {
/**
* Create a segment at the start of a request made with `undici` or `fetch`.
*
* That `message` must be `unknown` because that's the type expected by `subscribe`
*
* @param message The message received from the `undici` channel
*/
const onRequestStart = (message: unknown): void => {
const { request } = message as DiagnosticsChannel.RequestCreateMessage;
const parentSubsegment = this.getSegment();
const requestURL = getRequestURL(request);
if (parentSubsegment && requestURL) {
const method = request.method;
const subsegment = parentSubsegment.addNewSubsegment(
requestURL.hostname
);
subsegment.addAttribute('namespace', 'remote');
// addHeader is not part of the type definition but it's available:
// https://github.com/nodejs/undici/blob/main/docs/docs/api/DiagnosticsChannel.md#undicirequestcreate
// @ts-expect-error
request.addHeader(
'X-Amzn-Trace-Id',
`Root=${environmentVariablesService.getXrayTraceId()};Parent=${subsegment.id};Sampled=${subsegment.notTraced ? '0' : '1'}`
);
(subsegment as HttpSubsegment).http = {
request: {
url: `${requestURL.protocol}//${requestURL.hostname}${requestURL.pathname}`,
method,
},
};
this.setSegment(subsegment);
}
};
/**
* Enrich the subsegment with the response details, and close it.
* Then, set the parent segment as the active segment.
*
* `message` must be `unknown` because that's the type expected by `subscribe`
*
* @param message The message received from the `undici` channel
*/
// ... rest of the existing code ...
}
Steps to Reproduce
- Deploy a Lambda function instrumented using Powertools for AWS Lambda (TypeScript) with tracing enabled that uses undici to make downstream api calls.
- Configure the Lambda to be invoked via an SQS event source with a DLQ in place.
- Cause the Lambda function to fail (for example, by throwing an error) so that the message is retried.
- Observe that on each retry, the instrumentFetch method appends an additional X-Amzn-Trace-Id header.
- Over several retries, inspect the outgoing HTTP requests to find that the header has grown, eventually triggering 431 errors from nginx.
Possible Solution
- Modify the instrumentFetch implementation to check for an existing X-Amzn-Trace-Id header and either replace it or prevent appending multiple times.
- Alternatively, provide an option or configuration to reset the trace header on Lambda invocations, ensuring that only one trace header is sent per outgoing request.
- Consider truncating or simplifying the header, especially for retry scenarios, to ensure it stays within acceptable size limits.
Powertools for AWS Lambda (TypeScript) version
latest
AWS Lambda function runtime
22.x
Packaging format used
Lambda Layers
Execution logs
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Closed