Environment
@sentry/webpack-plugin 5.4.0 (latest), @sentry/bundler-plugins 10.70.0
@sentry/cli 2.58.6
- webpack 5.107.2, Node 22,
devtool: 'hidden-source-map'
- Sentry SaaS
Steps to Reproduce
-
Build with the upload disabled (disable: 'disable-upload') but injection left on:
// webpack.config.js
const path = require('path');
const { sentryWebpackPlugin } = require('@sentry/webpack-plugin');
module.exports = {
mode: 'production',
entry: path.resolve(__dirname, 'src/index.js'),
devtool: 'hidden-source-map',
output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' },
plugins: [
sentryWebpackPlugin({
org: 'my-org',
project: 'my-project',
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: { disable: 'disable-upload' },
}),
],
};
-
npx webpack
-
Inspect the emitted artifacts.
-
Do the "manual upload at a later point in time" the option is documented for:
$ sentry-cli sourcemaps upload --org my-org --project my-project \
--release 1.0.0 --debug-id-reference --strict dist
(--debug-id-reference is needed because the emitted .js carries only the runtime
marker and no //# debugId= comment, so the CLI has to read the ID from the map.)
-
Read the result back:
$ curl -H "Authorization: Bearer $TOKEN" \
".../projects/my-org/my-project/files/artifact-bundles/?query=1.0.0"
$ curl -H "Authorization: Bearer $TOKEN" \
".../projects/my-org/my-project/artifact-bundles/<bundleId>/files/"
Expected Result
Per the TSDoc for sourcemaps.disable:
If set to "disable-upload", the plugin will not upload sourcemaps to Sentry, but will inject debug IDs into the build artifacts. This is useful if you want to manually upload sourcemaps to Sentry at a later point in time.
I expected the emitted artifacts to be in a state a later manual upload can use — i.e. the debug ID readable from the emitted .js and/or its .js.map — so that step 4 produces a bundle that symbolicates.
Actual Result
The bundle is injected, but the source map is not, so the manual upload produces an artifact bundle in which every entry has debugId: null and nothing symbolicates.
$ npx webpack
webpack 5.107.2 compiled successfully
$ grep -o 'sentry-dbid-[0-9a-f-]\{36\}' dist/bundle.js
sentry-dbid-555cf081-0e3a-4edf-bd14-4b561d9f20e5 # injected, as documented
$ grep -c '//# debugId=' dist/bundle.js
0 # no spec comment either
$ node -e 'console.log(Object.keys(require("./dist/bundle.js.map")).join(", "))'
version, file, mappings, sources, sourcesContent, names, sourceRoot
# ^ no debug_id / debugId
Uploading that dist/ and reading it back (step 5), next to the two other paths for comparison:
| upload path |
artifact names |
debugId on .js |
debugId on .js.map |
plugin's own upload (disable-upload off) |
~/<debugId>-0.js |
set |
set |
sentry-cli ... --debug-id-reference dist on disable-upload output |
~/bundle.js |
null |
null |
same, after copying the marker's UUID into the map as debug_id/debugId |
~/bundle.js |
set |
set |
Cause
Stamping the map is part of the upload routine the flag switches off. prepareBundleForDebugIdUpload() writes both the //# debugId= comment and the map's debug_id into copies in a temp folder, uploads those, then deletes them — which is also why the first row above is named <debugId>-0.js rather than bundle.js.
uploadSourcemaps()'s own doc comment states it directly:
If prepareArtifacts is set to false, no preparation (e.g. adding //# debugId=... and writing adjusted source maps) is performed and no temp folder is used.
So disable-upload skips the preparation along with the upload, leaving undone precisely the half a manual upload depends on.
Why sentry-cli sourcemaps inject is not a general workaround
The docs recommend sentry-cli sourcemaps inject before upload for CLI uploads. Run after a disable-upload build, it fails in two independent ways:
- It mints a different ID.
inject does not recognise the plugin's sentry-dbid- marker, so it generates a fresh ID from file content — measured fdffc1a1-b558-5928-bbca-0cf1e824d253 against the plugin's 82d6f1cb-eba2-41b5-bef9-b265d8b83aab for the same bundle. At runtime the SDK reports the plugin's ID out of window._sentryDebugIds while Sentry has indexed inject's, so nothing matches.
- It rewrites the emitted
.js. Builds that pin subresource integrity computed during the compilation then break — we hash assets with webpack-assets-manifest and serve <script integrity="...">, so a post-build rewrite makes the browser refuse the bundle.
For builds using SRI, there is currently no supported way to complete disable-upload.
Suggestion
Any one of these would resolve it:
- Docs — state that only the bundles are injected, and that the maps must be stamped separately before a manual upload will work.
- Stamp the emitted maps when
disable-upload is set. The ID is already known at that point (derived from chunk.contentHash.javascript), and adding debug_id/debugId to the emitted map is inert for other source map consumers — devtools ignore unknown fields, and we measured identical originalPositionFor results before and after. This would make the option behave as documented.
- Expose
injectDebugIds() or prepareArtifacts through the bundler plugin options, so the preparation can run without the upload.
We currently ship a ~40-line webpack plugin doing option 2 for ourselves. Happy to open a PR if a maintainer indicates a preferred direction.
Possibly affected already
#927 and #868 both adopt disable-upload specifically in order to upload later with sentry-cli, which is the pattern above.
Environment
@sentry/webpack-plugin5.4.0 (latest),@sentry/bundler-plugins10.70.0@sentry/cli2.58.6devtool: 'hidden-source-map'Steps to Reproduce
Build with the upload disabled (
disable: 'disable-upload') but injection left on:npx webpackInspect the emitted artifacts.
Do the "manual upload at a later point in time" the option is documented for:
(
--debug-id-referenceis needed because the emitted.jscarries only the runtimemarker and no
//# debugId=comment, so the CLI has to read the ID from the map.)Read the result back:
Expected Result
Per the TSDoc for
sourcemaps.disable:I expected the emitted artifacts to be in a state a later manual upload can use — i.e. the debug ID readable from the emitted
.jsand/or its.js.map— so that step 4 produces a bundle that symbolicates.Actual Result
The bundle is injected, but the source map is not, so the manual upload produces an artifact bundle in which every entry has
debugId: nulland nothing symbolicates.Uploading that
dist/and reading it back (step 5), next to the two other paths for comparison:debugIdon.jsdebugIdon.js.mapdisable-uploadoff)~/<debugId>-0.jssentry-cli ... --debug-id-reference distondisable-uploadoutput~/bundle.jsdebug_id/debugId~/bundle.jsCause
Stamping the map is part of the upload routine the flag switches off.
prepareBundleForDebugIdUpload()writes both the//# debugId=comment and the map'sdebug_idinto copies in a temp folder, uploads those, then deletes them — which is also why the first row above is named<debugId>-0.jsrather thanbundle.js.uploadSourcemaps()'s own doc comment states it directly:So
disable-uploadskips the preparation along with the upload, leaving undone precisely the half a manual upload depends on.Why
sentry-cli sourcemaps injectis not a general workaroundThe docs recommend
sentry-cli sourcemaps injectbeforeuploadfor CLI uploads. Run after adisable-uploadbuild, it fails in two independent ways:injectdoes not recognise the plugin'ssentry-dbid-marker, so it generates a fresh ID from file content — measuredfdffc1a1-b558-5928-bbca-0cf1e824d253against the plugin's82d6f1cb-eba2-41b5-bef9-b265d8b83aabfor the same bundle. At runtime the SDK reports the plugin's ID out ofwindow._sentryDebugIdswhile Sentry has indexedinject's, so nothing matches..js. Builds that pin subresource integrity computed during the compilation then break — we hash assets withwebpack-assets-manifestand serve<script integrity="...">, so a post-build rewrite makes the browser refuse the bundle.For builds using SRI, there is currently no supported way to complete
disable-upload.Suggestion
Any one of these would resolve it:
disable-uploadis set. The ID is already known at that point (derived fromchunk.contentHash.javascript), and addingdebug_id/debugIdto the emitted map is inert for other source map consumers — devtools ignore unknown fields, and we measured identicaloriginalPositionForresults before and after. This would make the option behave as documented.injectDebugIds()orprepareArtifactsthrough the bundler plugin options, so the preparation can run without the upload.We currently ship a ~40-line webpack plugin doing option 2 for ourselves. Happy to open a PR if a maintainer indicates a preferred direction.
Possibly affected already
#927 and #868 both adopt
disable-uploadspecifically in order to upload later withsentry-cli, which is the pattern above.