A request may cross API Gateway, one Lambda, an event bus, SQS, and two more workers. AWS gives each execution its own request ID, but those IDs do not explain that the executions belong to one user action.
A correlation ID provides that connection.
Accept or create at the first trusted edge#
const incoming = event.headers["x-correlation-id"];
const correlationId = isSafeCorrelationId(incoming)
? incoming
: crypto.randomUUID();
Validate length and allowed characters. An unbounded caller-controlled value can pollute logs. Return the chosen ID in the response so a client can include it in a support report.
Every structured log created during the request receives the same value.
Put it in event metadata#
When the request publishes asynchronous work, carry correlation separately from business data:
type EventEnvelope<T> = {
eventId: string;
correlationId: string;
causationId?: string;
occurredAt: string;
payload: T;
};
eventId identifies this event. correlationId groups the wider operation. causationId can point to the request or event that directly caused it.
These identifiers have different jobs. A correlation ID must never be used as an idempotency key: one request may legitimately create several effects.
Preserve it on retries#
SQS retries should keep the original correlation ID because they continue the same operation. Each Lambda invocation may also log its AWS request ID and attempt number. That gives operators both the business chain and the exact execution.
For scheduled jobs with no upstream request, create a correlation ID per run. For fan-out work, keep the parent's correlation ID and give each child its own event ID.
Make propagation automatic#
HTTP and worker wrappers should establish a request-scoped logging context. Publishers should require metadata instead of hoping each feature remembers to copy it.
Correlation IDs do not replace distributed tracing, but they deliver immediate value in logs, user-facing errors, and queue diagnostics. The rule is simple: create once at the edge, propagate explicitly, and never change its meaning halfway through the system.