Describe the bug
Under experimental.bundledDev, a plugin's hotUpdate hook is invoked with this.environment undefined. Reading this.environment is the documented way to scope hotUpdate behavior (https://vite.dev/changes/hotupdate-hook), so plugins following the docs throw a TypeError on every file edit.
Mechanism, as far as we can tell from the source (checked on main and v8.1.5):
ROLLUP_HOOKS (packages/vite/src/node/constants.ts) includes watchChange but not hotUpdate.
injectEnvironmentToHooks (packages/vite/src/node/build.ts) wraps resolveId/load/transform explicitly and every other hook only if (ROLLUP_HOOKS.includes(hook)) — so no environment-bound wrapper exists for hotUpdate, and under bundled dev the hook executes with the engine's bare plugin context.
The consequence is worse than a crash: the throw is captured as a build error, the dev server keeps running, and it serves stale output on every subsequent edit until restart. (#23024 improved terminal printing for failed HMR updates, but the hook-contract violation and the stale serving remain.) For anyone running Vite behind a wrapper — hosted dev servers, CI preview environments — this presents as "HMR silently stopped working."
Real-world occurrence: @tanstack/start-plugin-core reads this.environment.name on the first line of its hotUpdate hook (plugin.ts) — deterministic breakage on @tanstack/start-plugin-core 1.171.17–1.171.34, tracked in TanStack/router#8062 (guard PR TanStack/router#8061). These versions are pulled transitively by current @tanstack/react-start releases, so the blast radius is every TanStack Start app trying bundled dev.
Related issues, which we believe are distinct facets of the same underlying gap:
Suggested behavior until #22956 lands (in order of preference)
- Bind the environment for
hotUpdate when plugins are handed to the full-bundle dev engine, even while the Phase 2 semantics (Bundled dev mode design doc and Road Map #22746) are still being designed.
- Or skip
hotUpdate hooks entirely under bundled dev, with a one-time warning naming the plugin — an explicitly unsupported hook should not run with a context that violates its documented contract.
- At minimum, make the failure loud and attributed, rather than degrading to stale serving.
Reproduction
https://github.com/TIMTOMTOP/tanstack-hotupdate-repro
Steps to reproduce
npm install && npm run dev (repro pins vite@8.2.1, @tanstack/react-start@1.168.44 → @tanstack/start-plugin-core@1.171.34)
- Set
experimental: { bundledDev: true } (already set in the repro config)
- Edit any source file
hotUpdate throws TypeError: Cannot read properties of undefined (reading 'name'); the error is captured as a build error and the dev server serves stale output for all subsequent edits until restart
System Info
vite: 8.2.1 (also verified the ROLLUP_HOOKS omission on v8.1.5 and main)
@tanstack/react-start: 1.168.44 (→ @tanstack/start-plugin-core 1.171.34)
See reproduction repository for the full manifest.
Used Package Manager
npm
Validations
Describe the bug
Under
experimental.bundledDev, a plugin'shotUpdatehook is invoked withthis.environmentundefined. Readingthis.environmentis the documented way to scopehotUpdatebehavior (https://vite.dev/changes/hotupdate-hook), so plugins following the docs throw aTypeErroron every file edit.Mechanism, as far as we can tell from the source (checked on
mainandv8.1.5):ROLLUP_HOOKS(packages/vite/src/node/constants.ts) includeswatchChangebut nothotUpdate.injectEnvironmentToHooks(packages/vite/src/node/build.ts) wrapsresolveId/load/transformexplicitly and every other hook onlyif (ROLLUP_HOOKS.includes(hook))— so no environment-bound wrapper exists forhotUpdate, and under bundled dev the hook executes with the engine's bare plugin context.The consequence is worse than a crash: the throw is captured as a build error, the dev server keeps running, and it serves stale output on every subsequent edit until restart. (#23024 improved terminal printing for failed HMR updates, but the hook-contract violation and the stale serving remain.) For anyone running Vite behind a wrapper — hosted dev servers, CI preview environments — this presents as "HMR silently stopped working."
Real-world occurrence:
@tanstack/start-plugin-corereadsthis.environment.nameon the first line of itshotUpdatehook (plugin.ts) — deterministic breakage on@tanstack/start-plugin-core1.171.17–1.171.34, tracked in TanStack/router#8062 (guard PR TanStack/router#8061). These versions are pulled transitively by current@tanstack/react-startreleases, so the blast radius is every TanStack Start app trying bundled dev.Related issues, which we believe are distinct facets of the same underlying gap:
experimental.bundledDev: pluginhotUpdateis called withoutserver,timestamporread#23125 — thehotUpdateoptions object missingserver/timestamp/read(this issue is about thethiscontext instead).experimental.bundledDev: virtual modules fail withUNRESOLVED_ENTRYwhen their lazy entry is compiled #23124 (closed, Phase 2 milestone).hotUpdateandhandleHotUpdatehook #22956 — the open PR adding ahotUpdateadapter for bundled dev, which presumably becomes the real fix. This issue is about the behavior of current releases until it lands.Suggested behavior until #22956 lands (in order of preference)
hotUpdatewhen plugins are handed to the full-bundle dev engine, even while the Phase 2 semantics (Bundled dev mode design doc and Road Map #22746) are still being designed.hotUpdatehooks entirely under bundled dev, with a one-time warning naming the plugin — an explicitly unsupported hook should not run with a context that violates its documented contract.Reproduction
https://github.com/TIMTOMTOP/tanstack-hotupdate-repro
Steps to reproduce
npm install && npm run dev(repro pinsvite@8.2.1,@tanstack/react-start@1.168.44→@tanstack/start-plugin-core@1.171.34)experimental: { bundledDev: true }(already set in the repro config)hotUpdatethrowsTypeError: Cannot read properties of undefined (reading 'name'); the error is captured as a build error and the dev server serves stale output for all subsequent edits until restartSystem Info
Used Package Manager
npm
Validations
experimental.bundledDev: pluginhotUpdateis called withoutserver,timestamporread#23125 /experimental.bundledDev: virtual modules fail withUNRESOLVED_ENTRYwhen their lazy entry is compiled #23124 — different facets, cross-linked above.)