X-Ray service maps that show a lonely Lambda with no downstream edges are usually instrumentation gaps—not “the network vanished.” The unfair advantage is closing Node gaps: AWS SDK v3 middleware, undici/fetch, and async context loss so edges match reality during sev-1 triage.
⚡ TL;DR: Use ADOT or Powertools with SDK v3 capture; instrument undici explicitly; propagate
X-Amzn-Trace-Id/ W3C traceparent across HTTP; never trust a map that lacks edges you know exist—verify with raw traces. Pair with Lambda ADOT vs Powertools and OpenTelemetry for LLMs.
Common Node edge killers
| Gap | Symptom on map | Fix |
|---|---|---|
| SDK v3 without middleware | No DynamoDB/S3 edges | @aws-sdk/middleware-instrumentation / ADOT auto |
Global fetch / undici |
No HTTP peers | OTel undici instrumentation or wrap dispatcher |
Fire-and-forget void promise |
Truncated subsegments | Await or attach to parent context |
| Lambda response streaming | Partial traces | Ensure segment closed on finally |
| Manual HTTP without header | Broken trace ID | Inject trace header |
// ✅ AWS SDK v3 — ensure X-Ray / OTel middleware is registered once
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";
const ddb = new DynamoDBClient({
requestHandler: new NodeHttpHandler({ connectionTimeout: 2000, requestTimeout: 5000 }),
});
// With ADOT Lambda layer + env AWS_OTEL / instrumentations enabled, edges appear.
// ✅ undici: instrument Agent used by fetch
import { Agent, fetch as undiciFetch } from "undici";
import { diag } from "@opentelemetry/api";
const agent = new Agent({ connect: { timeout: 2000 } });
// Register @opentelemetry/instrumentation-undici against this Agent in tracing bootstrap
export async function callPeer(url: string, parentHeaders: HeadersInit) {
const res = await undiciFetch(url, { dispatcher: agent, headers: parentHeaders });
// ❌ bare fetch() without instrumentation → invisible edge
return res;
}
AsyncLocalStorage / context loss
// ❌ Loses parent subsegment
setImmediate(() => ddb.send(cmd));
// ✅ Bind context (OTel context.with / X-Ray captureAsyncFunc)
import { context, trace } from "@opentelemetry/api";
const ctx = context.active();
setImmediate(() => context.with(ctx, () => ddb.send(cmd)));
Triage habit when maps lie
- Open a single trace ID from the failing request (ALB/Lambda log).
- Confirm subsegments exist in raw JSON even if map collapsed them.
- Diff instrumented vs missing clients (search for
new Agent, rawhttp.request). - Fix library bootstrap—not the map UI.
Closing checklist
✅ Dos
– ✅ Boot tracing before importing app routes
– ✅ Instrument SDK v3 and undici explicitly
– ✅ Propagate trace headers on every egress
– ✅ Sample higher during incidents (X-Ray Sampling)
– ✅ Canary a trace that must include DDB+HTTP edges in CI
❌ Don’ts
– ❌ Don’t trust empty service maps over logs
– ❌ Don’t create clients lazily after disabling auto-instrumentation
– ❌ Don’t swallow errors inside fire-and-forget tasks
– ❌ Don’t mix X-Ray and OTel exporters without a clear gateway story
Related reading
- Lambda ADOT vs Powertools: Tracing Tradeoffs on Node 20
- Lambda X-Ray Sampling: Cost Versus Debuggability
- OpenTelemetry Sampling for Node
- Node undici Dispatcher Pools
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
