Skip to content

Vite 8.0.11 dev server: __BUNDLED_DEV__ and __SERVER_FORWARD_CONSOLE__ pass through unreplaced in /@vite/client (browser ReferenceError) #22419

Description

@cflavio-xtrakt

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions