JS SDK Reference
Debug IDs

Debug IDs

Debug IDs tie a deployed bundle to the exact source map produced by the same build. At build time, @tracewayapp/bundler-plugin embeds a unique 128-bit ID (UUID format) into both the bundle and its .map file. At runtime the SDK reports the IDs of the bundles involved in each exception, and the backend uses them to pick the matching map: content-addressed, with no dependence on filenames, URLs, versions, or deploy timing.

This is the same mechanism Sentry pioneered and the ECMA-426 source map specification's Debug ID proposal (opens in a new tab) standardizes: a debugId field in the source map JSON and a //# debugId=<uuid> comment in the generated file. Traceway emits and consumes exactly that format, so artifacts are interoperable with other ECMA-426 tooling, and Traceway accepts artifacts produced by other debug-ID-aware tools (Sentry's bundler plugins, sentry-cli sourcemaps inject, Rollup's output.sourcemapDebugIds, Bun).

Without debug IDs, maps are matched by filename, which works with content-hashed bundle names but breaks down with stable names like app.js served from multiple concurrent deploys. See How Maps Are Matched.

Installation

npm install -D @tracewayapp/bundler-plugin

Vite

// vite.config.ts
import { defineConfig } from "vite";
import { tracewayDebugIds } from "@tracewayapp/bundler-plugin/vite";
 
export default defineConfig({
  build: {
    sourcemap: true,
  },
  plugins: [tracewayDebugIds()],
});

Rollup

// rollup.config.js
import { tracewayDebugIds } from "@tracewayapp/bundler-plugin/rollup";
 
export default {
  output: {
    sourcemap: true,
  },
  plugins: [tracewayDebugIds()],
};

webpack

// webpack.config.js
const {
  TracewayDebugIdsWebpackPlugin,
} = require("@tracewayapp/bundler-plugin/webpack");
 
module.exports = {
  devtool: "source-map",
  plugins: [new TracewayDebugIdsWebpackPlugin()],
};

Requires webpack 5.

Upload As Usual

The upload step is unchanged; the backend detects debug IDs in the uploaded files automatically:

npx traceway-sourcemaps \
  --url https://your-traceway-instance.com \
  --token your-sourcemap-token \
  --directory ./dist

How It Works

For every emitted JS chunk, the plugin:

  1. Derives a deterministic debug ID from the chunk's content (a SHA-256 hash formatted as a UUID), so rebuilding identical input yields the identical ID.
  2. Injects a small self-executing snippet at the top of the chunk (after any shebang and "use strict" directive) that registers the ID in a _tracewayDebugIds global, 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. Appends a //# debugId=<uuid> comment to the chunk, above the //# sourceMappingURL= comment.
  4. Writes the ID into the source map JSON as debugId (and debug_id for compatibility with Sentry-era tooling).

When an exception is captured, the SDK reads _tracewayDebugIds (and _sentryDebugIds, so bundles processed by Sentry tooling work too), maps each bundle filename in the stack trace to its debug ID, and sends that mapping with the exception. At ingest, the backend resolves each frame against the map uploaded with that exact debug ID, falling back to filename matching when no debug-ID artifact exists. Uploaded artifacts carrying a debug ID are stored under both their filename and a by-debug-id/ key, so old SDK versions and frames without IDs keep resolving.

Node and OpenTelemetry

Debug IDs work the same way for server-side bundles: the injected snippet detects globalThis/global, and Node stack traces carry the bundle path just like browser ones. If you bundle a Node service with Vite, Rollup, or webpack, the same plugin applies.

OpenTelemetry currently has no debug ID convention: there is no semantic-conventions attribute for source map linkage, and the JS SDKs do not collect anything like it. Traceway therefore carries debug IDs in its own report payload (a debugIds filename-to-ID map on each exception). Because the build artifacts follow the ECMA-426 format rather than anything Traceway-specific, a future Node/OTel integration can adopt the same uploads without rebuilding anything.

Interoperability Notes

  • Rollup/Vite native IDs: Rollup's output.sourcemapDebugIds: true emits the comment and map field but no runtime registry, so the SDK cannot report IDs at runtime from that alone. The Traceway plugin is a superset; you do not need both.
  • Sentry tooling: bundles processed by @sentry/vite-plugin, @sentry/webpack-plugin, or sentry-cli sourcemaps inject register _sentryDebugIds, which the Traceway SDK also reads. Maps carrying only the legacy debug_id field are accepted on upload.
  • esbuild: the Traceway plugin does not cover esbuild, and esbuild emits no debug IDs natively. Bundles processed by @sentry/esbuild-plugin (or a post-build sentry-cli sourcemaps inject) get _sentryDebugIds injected, which the SDK reads as described above, so esbuild apps can still get exact debug-ID matching through that tooling; without it they fall back to filename matching.

Next Steps