Skip to content

Bug: X-Amzn-Trace-Id Header Growth in instrumentFetch Causes 431 Errors When Using SQS Triggers #3692

Closed as not planned
@drewjohnston-0000

Description

@drewjohnston-0000

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

No one assigned

    Labels

    not-a-bugNew and existing bug reports incorrectly submitted as bugrejectedThis is something we will not be working on. At least, not in the measurable futuretracerThis item relates to the Tracer Utility

    Type

    No type

    Projects

    Status

    Closed

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions