Describe the bug
On Vite v8.0.11 the dev server serves /@vite/client (i.e. node_modules/vite/dist/client/client.mjs) with two build-time constants unreplaced, so the browser throws Uncaught ReferenceError: __BUNDLED_DEV__ is not defined before main.ts runs and the entire app fails to boot.
Specifically, in the chain inside clientInjectionsPlugin.createClientConfigValueReplacer:
.replace(`__WS_TOKEN__`, wsTokenReplacement) // ✅ replaced
.replace(`__SERVER_FORWARD_CONSOLE__`, serverForwardConsoleReplacement) // ❌ NOT replaced in served output
.replaceAll(`__BUNDLED_DEV__`, bundleDevReplacement) // ❌ NOT replaced in served output
__WS_TOKEN__ does get replaced in the served file (verified via curl), but the two new constants added in #20916 and #21235 pass through verbatim.
Reproduction
Hard to share a public minimal repro right now (private monorepo), but the symptom reproduces 100% on a Vite 8.0.11 project with a standard vite.config.ts:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: { host: '127.0.0.1', port: 5177 },
})
$ npm run dev
$ curl http://localhost:5177/@vite/client | grep -E 'wsToken|isBundleMode|forwardConsole'
const wsToken = \"7095Bis0DD6P\"; # ✅ replaced
const isBundleMode = __BUNDLED_DEV__; # ❌ raw identifier
const forwardConsole = __SERVER_FORWARD_CONSOLE__; # ❌ raw identifier
The browser then throws:
Uncaught ReferenceError: BUNDLED_DEV is not defined
…which kills HMR setup before main.ts runs.
System info
- Vite 8.0.11 (latest as of 2026-05-10)
- Node 22
- macOS 25.4 (arm64)
- @vitejs/plugin-vue 5.x
- Plain dev server, no SSR, no
bundledDev / no forwardConsole user config
Workaround
Adding the two constants to user define in vite.config.ts papers over it (Vite's __DEFINES__ mechanism kicks in for user defines and replaces them at runtime):
export default defineConfig({
plugins: [vue()],
define: {
__BUNDLED_DEV__: 'false',
__SERVER_FORWARD_CONSOLE__: 'false',
},
})
After restart, curl /@vite/client shows:
const isBundleMode = false;
const forwardConsole = {\"enabled\":true,...};
…and the browser boots normally. But this is clearly a workaround — the dedicated __BUNDLED_DEV__ / __SERVER_FORWARD_CONSOLE__ replacement chain in clientInjectionsPlugin should handle this without user intervention.
What's surprising
The other constants on the same .replace() chain (__WS_TOKEN__, __HMR_*, __MODE__, __BASE__) all get replaced correctly in the served output. Only these two are skipped — even though they're in the same function in createClientConfigValueReplacer. Suggests either:
escapeReplacement(undefined) ends up returning a function that signals "don't replace" (the function returns JSON.stringify(undefined) which is the value undefined not a string — but I expected JS to coerce to \"undefined\")
- or the values of
config.server.forwardConsole / config.experimental.bundledDev are accessed before they're initialized (race in plugin lifecycle)
Related PRs
Happy to put together a public minimal repro if it helps.
Describe the bug
On Vite v8.0.11 the dev server serves
/@vite/client(i.e.node_modules/vite/dist/client/client.mjs) with two build-time constants unreplaced, so the browser throwsUncaught ReferenceError: __BUNDLED_DEV__ is not definedbeforemain.tsruns and the entire app fails to boot.Specifically, in the chain inside
clientInjectionsPlugin.createClientConfigValueReplacer:__WS_TOKEN__does get replaced in the served file (verified via curl), but the two new constants added in #20916 and #21235 pass through verbatim.Reproduction
Hard to share a public minimal repro right now (private monorepo), but the symptom reproduces 100% on a Vite 8.0.11 project with a standard
vite.config.ts:The browser then throws:
…which kills HMR setup before
main.tsruns.System info
bundledDev/ noforwardConsoleuser configWorkaround
Adding the two constants to user
defineinvite.config.tspapers over it (Vite's__DEFINES__mechanism kicks in for user defines and replaces them at runtime):After restart,
curl /@vite/clientshows:…and the browser boots normally. But this is clearly a workaround — the dedicated
__BUNDLED_DEV__/__SERVER_FORWARD_CONSOLE__replacement chain inclientInjectionsPluginshould handle this without user intervention.What's surprising
The other constants on the same
.replace()chain (__WS_TOKEN__,__HMR_*,__MODE__,__BASE__) all get replaced correctly in the served output. Only these two are skipped — even though they're in the same function increateClientConfigValueReplacer. Suggests either:escapeReplacement(undefined)ends up returning a function that signals "don't replace" (the function returnsJSON.stringify(undefined)which is the valueundefinednot a string — but I expected JS to coerce to\"undefined\")config.server.forwardConsole/config.experimental.bundledDevare accessed before they're initialized (race in plugin lifecycle)Related PRs
__SERVER_FORWARD_CONSOLE__)__BUNDLED_DEV__)Happy to put together a public minimal repro if it helps.