JS SDK Reference (coming soon)
Debug IDs

Debug IDs

A debug ID ties a deployed bundle to the exact source map produced by the same build. The build embeds a 128-bit ID (in UUID format) into both the bundle and its .map file; the client reports the IDs of the bundles involved in an exception; and the backend picks the map uploaded with that exact ID. Matching is content-addressed, with no dependence on filenames, URLs, versions or deploy timing.

⚠️

Not usable end-to-end yet. The backend already accepts debug IDs: an uploaded map or bundle carrying one is stored under a by-debug-id/ key alongside its filename, and frames that arrive with a debug ID resolve against it. What is missing is the client end — the TracePath browser SDK, which is what reports a frame's debug ID at capture time, is not published yet. Until it ships, maps resolve by filename, which is reliable as long as your bundle names are content-hashed.

The format

TracePath uses the format standardized by the ECMA-426 source map specification's Debug ID proposal (opens in a new tab): a debugId field in the source map JSON and a //# debugId=<uuid> comment in the generated file. Nothing about it is TracePath-specific, so artifacts stay interoperable with other ECMA-426 tooling, and TracePath accepts artifacts produced by other debug-ID-aware tools.

On upload, the backend scans each file for both: the debugId field in a .map, and the //# debugId= or //@ debugId= comment in a bundle. The legacy debug_id field written by Sentry-era tooling is accepted too. An ID has to be a well-formed lowercase UUID to be indexed.

Why filenames are not enough

Without debug IDs, a map is matched to a frame by filename. That works well with content-hashed bundle names — index-DZ8aDdLF.js — because every build produces a distinct name and the maps never collide. It breaks down with stable names like app.js served from two concurrent deploys: the most recent upload wins, and half your traces resolve against the wrong build. Debug IDs remove the ambiguity entirely.

How the runtime half will work

When the browser SDK ships, the build step will, for each emitted JS chunk:

  1. Derive a deterministic debug ID from the chunk's content, so rebuilding identical input yields an identical ID.
  2. Inject a small self-executing snippet at the top of the chunk that registers the ID in a global registry, keyed by the stack of a synthetic error — which is how the SDK later discovers the runtime URL each bundle was loaded from. Source maps are adjusted for the insertion, so resolved positions are unaffected.
  3. Append the //# debugId=<uuid> comment above the //# sourceMappingURL= comment.
  4. Write the ID into the source map JSON.

At capture time the SDK reads that registry, maps each bundle filename in the stack trace to its debug ID, and sends the mapping with the exception. At ingest, the backend resolves each frame against the map uploaded with that debug ID, falling back to filename matching when no debug-ID artifact exists. Because artifacts are stored under both keys, frames without IDs keep resolving from the same upload.

Uploading

Nothing changes about the upload itself: debug IDs are detected server-side in the file content, with no extra fields or flags. See Source Maps for the endpoint and its limits.

OpenTelemetry

OpenTelemetry has no debug ID convention: there is no semantic-conventions attribute for source map linkage, and the OTel JS SDKs collect nothing like it. TracePath therefore carries debug IDs in its own report payload rather than in OTLP. Because the build artifacts follow the ECMA-426 format rather than anything TracePath-specific, the same uploads will work unchanged if an OTel convention appears later.

Next steps