Skip to content

Sentry config silently breaks client source maps: sourcemaps in unstable_sentryVitePluginOptions #1094

Description

@lasseklovstad

The Sentry config in vite.config.ts triggers an upstream bug in @sentry/react-router that ships two different debug IDs in every client chunk, only one of which has a source map uploaded to Sentry. The one that wins at runtime is the one without a map, so client-side stack traces in Sentry arrive minified (js_no_source).

The trigger is passing sourcemaps through unstable_sentryVitePluginOptions:

unstable_sentryVitePluginOptions: {
	release: { name: process.env.COMMIT_SHA, setCommits: { auto: true } },
	sourcemaps: {
		filesToDeleteAfterUpload: ['./build/**/*.map'],   // <-- enough to trigger it
	},
},

@sentry/react-router sets sourcemaps: { disable: true } on the underlying Vite plugin, because debug ID injection and upload are handled later by sentryOnBuildEnd (which this repo wires up in react-router.config.ts). But a trailing ...unstable_sentryVitePluginOptions spread re-assigns the whole sourcemaps key, dropping disable. The Vite plugin then injects its own debug IDs, uploads maps under them, and deletes every client .map via the glob above — after which sentry-cli finds chunks with no adjacent map and appends a second debug ID with nothing to upload. That second one is what ships.

I've reported it upstream with a reproduction: getsentry/sentry-javascript#22929. There's an existing fix in a closed PR there (#20042), but it isn't merged, and main still has the bug.

Suggested fix

Independent of whether upstream lands the patch — the top-level fields are the supported surface, and release there accepts both name and setCommits, so nothing is lost:

const sentryConfig: SentryReactRouterBuildOptions = {
	authToken: process.env.SENTRY_AUTH_TOKEN,
	org: process.env.SENTRY_ORG,
	project: process.env.SENTRY_PROJECT,

	release: {
		name: process.env.COMMIT_SHA,
		setCommits: { auto: true },
	},
}

Dropping sourcemaps entirely is deliberate: sentryOnBuildEnd then falls back to its own default of `${reactRouterConfig.buildDirectory}/**/*.map`, which is correct by construction and doesn't need to track the build directory by hand.

Worth knowing that this fails silently in both directions, which is why it's easy to have and not notice: the upload succeeds and logs success (under the debug ID that loses), and the .map cleanup still happens as intended, so the build output looks entirely correct. It only surfaces once a client-side error actually reaches Sentry — server frames are unaffected.

Possibly worth pairing with it

build.sourcemap: true emits a //# sourceMappingURL= comment in every shipped chunk, and build/client/assets is served publicly. Today the maps themselves are deleted after upload so the comments dangle rather than expose anything — but that cleanup only runs when SENTRY_AUTH_TOKEN is set and NODE_ENV=production, so any production-mode build without the token keeps its maps next to a comment pointing straight at them.

build.sourcemap: 'hidden' still emits the maps for upload but omits the comment. It's Sentry's own recommended setting (their plugin defaults to it when unset), and symbolication is unaffected since frames are matched by debug ID, not by the comment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions