Skip to content

feat(sourcemap): Add wasm sourcemap support for inject and upload - #1629

Draft
sentry-junior[bot] wants to merge 7 commits into
port/cli-1572-wasm-uploadfrom
port/cli-1588-wasm-sourcemaps
Draft

sentry-junior[bot] wants to merge 7 commits into
port/cli-1572-wasm-uploadfrom
port/cli-1588-wasm-sourcemaps

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Ported from getsentry/cli#1588 by @d2anamaria, which is being closed as part of the getsentry/cli archive. This PR is stacked on #1628 (the port of cli#1572) and should merge after it. It is a draft because the original was a draft, and nobody had reviewed it yet.

Problem

sentry sourcemap inject and sentry sourcemap upload only look for .js, .cjs, and .mjs files. When Emscripten builds with -gsource-map, it writes an app.wasm.map file, and these commands never upload it. Even when the map gets to Sentry some other way, nothing links it to its module. Events identify a wasm module by its build_id custom section, but uploaded sourcemaps are matched by the debug_id inside the map JSON.

Solution

Both commands now handle <stem>.wasm + <stem>.wasm.map pairs next to JavaScript pairs.

  • The module's build_id decides the id. The CLI formats it as a dashed UUID and writes it into the map as debug_id / debugId.
  • A module with no build_id gets a random one first.
  • The module itself is never read as text.
  • Running the commands again changes nothing. If a map already has a different id, the module's id replaces it.
  • upload sends the module as minified_source and the map as source_map, both under the same id. --url-prefix, --strip-prefix, --strip-common-prefix, --release, and --dist all work for these pairs.
  • *.debug.wasm companions are skipped, because debug-files wasm-upload uploads them.
  • A directory without wasm behaves exactly as before.

Build-id stamping moves to lib/wasm/stamp.ts, so wasm-upload and the sourcemap path follow the same rule.

Port notes

  • Conflicts were resolved against toolkit's newer runtime-registration and inline-sourcemap handling. The help text keeps toolkit's wording, and --strip-common-prefix still handles inline maps.
  • setSourcemapDebugId calls toolkit's mutateSourcemap without snippetLine, so mappings are not shifted. In the original PR this was the offsetMappings: false option.
  • Bot findings on cli#1588, now fixed:
    • With --dry-run, wasm pairs showed as unchanged even when the command would stamp them. mapWritten and moduleStamped now report what a real run would do, as injected does for JS.
    • addWasmDiscoveryCounts ignoring --ignore / --ignore-file: kept on purpose. The JS diagnostic doesn't apply them either, and the .wasm.map count is subtracted from the JS tally, so both walks have to see the same files.
  • Merged with main: a glob upload target (sentry sourcemap upload './dist/**') narrows wasm pairs the same way it narrows JS pairs. A pair is kept when the module or its map matches.
  • Known limitations, carried over from the original PR:
    • Pairs are found by filename only. The module's sourceMappingURL section is not parsed.
    • Inline (data-URL) wasm sourcemaps are not supported.

sentry-cli v3 compatibility

  • v3 sourcemaps upload includes map in its default extensions, so it uploaded app.wasm.map by URL. v4 on main drops it. This PR brings it back, paired with the module.
  • v3 never wrote to .wasm files from sourcemaps inject/upload. This PR stamps a build_id into modules that have none. --no-rewrite keeps the v3 behavior: nothing is written, and the pair uploads by URL with whatever id the module already has.

build_id convention (getsentry/symbolic#1069)

The map is keyed by the convention-correct id, decoded from the build_id vector. The other readers now agree:

Caveats:

  • Events from SDKs older than 11.11.0 still carry the shifted id, so they won't match these maps.
  • @sentry/wasm 11.12.0 strips the length byte only from 16-byte ids. Modules linked with wasm-ld --build-id=sha1 (20 bytes) still report a shifted id, and that has to be fixed in the SDK.
  • Nothing resolves wasm frames through maps yet (symbolicator-js has no wasm path), so for now this upload lays groundwork.

Review fixes

  • A bare 16-byte build_id (written before the length prefix was standardized) is read as is, matching symbolic. Before, such a module got a second, random build_id, and symbolic and the SDK then read different ones. The fix is in feat(debug-files): Add wasm-upload command for WASM debug setup #1628 (lib/wasm/binary.ts) and is merged here.
  • A pair that can't be handled is skipped with a warning and left out of the upload: a .wasm that isn't a module, a map that isn't JSON, an unreadable build_id, or files that can't be written. Before, it threw after the JS files had already been rewritten. The map is checked before the module is stamped, so a skipped pair writes nothing.
  • Maps saved with a BOM are accepted.
  • A build_id too short to form a debug ID produces a warning.
  • Modules and maps are replaced through a temp file and rename, so an interrupted write can't leave a corrupt artifact.

End-to-end check

Real Emscripten builds were run through the bundled CLI from this branch, uploading to a mock Sentry. Each module was then crashed in headless Chromium with @sentry/wasm.

build_id symbolic 14.0.0-alpha.5 @sentry/wasm 11.12.0 11.10.0 Frame through the uploaded map
--build-id=uuid / fast ✅ ✅ ❌ shifted crash.c:7:4
--build-id=sha1 (20 bytes) ✅ ❌ SDK bug ❌ crash.c:7:4
none (CLI stamps one) ✅ ✅ ❌ crash.c:7:4
legacy bare 16 bytes ✅ ✅ ✅ crash.c:7:4

Validation

  • tsc --noEmit for packages/cli
  • vitest for test/lib and test/commands: everything passes except 6 cases that fail only in this sandbox (root permissions, delta-upgrade e2e) and are unrelated to this change
  • ci:policy
  • oxlint and oxfmt --check for packages/cli
  • generate:docs leaves no diff

Refs getsentry/cli#1588

via BYK, @betegon.

--

View Session [Sentry]

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Nov 12, 2026 3:56pm UTC

Request Review

`sourcemap inject` and `sourcemap upload` only discovered `.js`, `.cjs`,
and `.mjs`, so the `app.wasm.map` Emscripten writes with `-gsource-map`
never reached Sentry. Even when it did, nothing linked it to its module:
events identify a wasm module by its `build_id` custom section, while
sourcemaps match by the `debug_id` inside the map JSON.

Both commands now handle `<stem>.wasm` + `<stem>.wasm.map` pairs next to
JS pairs. The module's `build_id` is the source of truth. The CLI formats
it as a dashed UUID and writes it onto the map as `debug_id`/`debugId`,
stamping a random `build_id` first when the module has none. The module
is never read as text. Running it again changes nothing, and if the map
already has a different id, the module's id replaces it. `upload` sends
the module as `minified_source` and the map as `source_map` under that
id. `*.debug.wasm` companions are skipped because `debug-files
wasm-upload` owns them.

Build id stamping moves to `lib/wasm/stamp.ts` so `wasm-upload` and the
sourcemap path share one rule.

Ported from getsentry/cli#1588, which is being closed with the repo
archive. Stacked on the getsentry/cli#1572 port.
@sentry-junior
sentry-junior Bot force-pushed the port/cli-1588-wasm-sourcemaps branch from 9a44113 to 39c9a29 Compare October 23, 2026 14:03
Bring in main through the rebased #1628 and fit wasm pairs into what landed
there:

- `sourcemap upload <glob>` now narrows wasm pairs the way it narrows JS
  pairs: a pair is kept when the module or its map matches, and the
  zero-pairs diagnostic counts only matching files so the `.wasm.map`
  subtraction lines up with the JS tally.
- `--dry-run` now reports what a real run would change. `mapWritten` and
  `moduleStamped` were always false in a dry run, so `sourcemap inject
  --dry-run` counted every wasm pair as skipped. `setSourcemapDebugId`
  returns `changed` instead of `written` to say the same in both modes.
- `relativePosix` is shared with JS discovery instead of duplicated.

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
Co-Authored-By: BYK <burak.kaya@sentry.io>
sentry-junior Bot and others added 2 commits November 12, 2026 12:55
…cemaps

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
A .wasm that isn't a module or a .wasm.map that isn't JSON threw a raw
error after the JS files had already been rewritten, so nothing was
uploaded and the crash reached Sentry as unexpected. Skip such a pair
with a warning, as the JS path does for a bad inline map, and leave it
out of the upload. Also skip a module whose build_id section exists but
can't be read: stamping it would add a second build_id, and symbolic and
@sentry/wasm read different ones.

Check the map before stamping so a skipped pair writes nothing, accept
maps saved with a BOM, warn when a build_id is too short to form a debug
ID, and replace modules and maps through a temp file and rename so an
interrupted write can't leave a corrupt artifact.

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
Co-Authored-By: BYK <burak.kaya@sentry.io>
sentry-junior Bot and others added 2 commits November 12, 2026 15:54
…cemaps

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
wasm-upload now only treats <stem>.<build_id>.debug.wasm as its own
companion, so a plain app.debug.wasm debug build gets paired with its
map instead of being skipped. Update the fixtures and cover that case.

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
Preview — a4e82449 Deployed Nov 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants