Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/tanstackstart-react
SDK Version
10.72.0 (also reproduced on 10.69.0)
Framework Version
vite 8.2, nitro 3.0.260610-beta (Vite plugin mode), Node 24
Steps to Reproduce
makeEnableSourceMapsVitePlugin (the sentry-tanstackstart-react-source-maps plugin) returns the entire user config from its Vite config hook:
config(viteConfig) {
return {
...viteConfig,
build: {
...viteConfig.build,
sourcemap: getUpdatedSourceMapSettings(viteConfig, options),
},
};
}
Per the Vite plugin docs, the config hook should return a partial object that gets merged into the existing config. Returning the full config merges the config into itself. When Nitro's Vite plugin is present, this duplicates its internal asset processing, and every serverAssets file gets embedded as wrapped module source instead of file content.
Minimal repro (no TanStack app needed, the plugin is enough):
package.json deps: nitro@3.0.260610-beta, vite@^8.2.0, @sentry/tanstackstart-react@10.72.0
vite.config.ts:
import { defineConfig } from 'vite';
import { nitro } from 'nitro/vite';
import { sentryTanstackStart } from '@sentry/tanstackstart-react/vite';
export default defineConfig({
plugins: [
nitro({
serverAssets: [{ baseName: 'test', dir: './assets' }],
handlers: [{ route: '/asset-check', handler: './routes/asset-check.ts' }],
}),
sentryTanstackStart({ autoInstrumentMiddleware: false, telemetry: false }),
],
});
assets/hello.txt: hello asset content\n
routes/asset-check.ts:
import { defineHandler } from 'nitro';
import { useStorage } from 'nitro/storage';
export default defineHandler(async () => {
const item = await useStorage('assets:test').getItem('hello.txt');
return { type: typeof item, length: (item as string).length, preview: JSON.stringify(item) };
});
Then vite build and run the output server.
Isolation steps I ran:
- No Sentry plugin at all: asset is correct.
- Plain
sentryVitePlugin from @sentry/vite-plugin: correct (it only adds the debug id banner).
- Only the
sentry-tanstackstart-react-source-maps sub plugin: broken.
- A bare test plugin with the same
config(c) { return { ...c, build: { ...c.build, sourcemap: 'hidden' } } }: broken, with zero Sentry code involved. So the full config spread is the trigger.
- Same test plugin returning only
{ build: { sourcemap: 'hidden' } }: correct.
Expected Result
GET /asset-check returns the file content:
{"type":"string","length":20,"preview":"\"hello asset content\\n\""}
Actual Result
useStorage('assets:test').getItem('hello.txt') returns the wrapped module source:
{"type":"string","length":38,"preview":"\"export default \\\"hello asset content\\\\n\\\"\""}
The built server bundle contains var hello_default = "export default \"hello asset content\\n\"" instead of the content.
This is silent data corruption. We found it in production because an executable bundle we ship as a Nitro server asset turned into an inert module, and our LLM prompt files shipped with an export default " prefix and escaped newlines.
Suggested fix, return a partial config as the Vite docs describe:
config(viteConfig) {
return {
build: { sourcemap: getUpdatedSourceMapSettings(viteConfig, options) },
};
}
I verified this exact change fixes the repro. Happy to send a PR if useful.
Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/tanstackstart-react
SDK Version
10.72.0 (also reproduced on 10.69.0)
Framework Version
vite 8.2, nitro 3.0.260610-beta (Vite plugin mode), Node 24
Steps to Reproduce
makeEnableSourceMapsVitePlugin(thesentry-tanstackstart-react-source-mapsplugin) returns the entire user config from its Viteconfighook:Per the Vite plugin docs, the
confighook should return a partial object that gets merged into the existing config. Returning the full config merges the config into itself. When Nitro's Vite plugin is present, this duplicates its internal asset processing, and everyserverAssetsfile gets embedded as wrapped module source instead of file content.Minimal repro (no TanStack app needed, the plugin is enough):
package.json deps:
nitro@3.0.260610-beta,vite@^8.2.0,@sentry/tanstackstart-react@10.72.0vite.config.ts:
assets/hello.txt:
hello asset content\nroutes/asset-check.ts:
Then
vite buildand run the output server.Isolation steps I ran:
sentryVitePluginfrom@sentry/vite-plugin: correct (it only adds the debug id banner).sentry-tanstackstart-react-source-mapssub plugin: broken.config(c) { return { ...c, build: { ...c.build, sourcemap: 'hidden' } } }: broken, with zero Sentry code involved. So the full config spread is the trigger.{ build: { sourcemap: 'hidden' } }: correct.Expected Result
GET /asset-checkreturns the file content:{"type":"string","length":20,"preview":"\"hello asset content\\n\""}Actual Result
useStorage('assets:test').getItem('hello.txt')returns the wrapped module source:{"type":"string","length":38,"preview":"\"export default \\\"hello asset content\\\\n\\\"\""}The built server bundle contains
var hello_default = "export default \"hello asset content\\n\""instead of the content.This is silent data corruption. We found it in production because an executable bundle we ship as a Nitro server asset turned into an inert module, and our LLM prompt files shipped with an
export default "prefix and escaped newlines.Suggested fix, return a partial config as the Vite docs describe:
I verified this exact change fixes the repro. Happy to send a PR if useful.