Skip to content

fix: avoid null-compiler crash in handleHotUpdate during vite.config restart - #835

Open
HeldvonKosmos wants to merge 1 commit into
vitejs:mainfrom
HeldvonKosmos:fix/handle-hot-update-null-compiler
Open

HeldvonKosmos wants to merge 1 commit into
vitejs:mainfrom
HeldvonKosmos:fix/handle-hot-update-null-compiler

Conversation

@HeldvonKosmos

@HeldvonKosmos HeldvonKosmos commented Aug 28, 2026

Copy link
Copy Markdown

Description

options.value.compiler is only assigned in the buildStart hook, not in configResolved. Vite's restartServer() (triggered by any change to vite.config.*) creates the new watcher and plugin
instances via _createServer(..., { listen: false }), but only calls buildStart later, inside server.listen(). The gap between those two points — spent awaiting server.close() of the old server
— is a window in which a file-change event can reach handleHotUpdate on a plugin instance whose compiler is still null.

handleHotUpdate dereferenced options.value.compiler.invalidateTypeCache unconditionally, before the file filter was even checked, so it threw:

TypeError: null is not an object
(evaluating 'options.value.compiler.invalidateTypeCache')

for any changed file, not just .vue files, surfacing as Vite's HMR overlay.

Reproduction

touch vite.config.ts; sleep <0.1-0.8>; touch <any file>

Deterministic on a project whose watched tree is large enough that server.close() measurably outlasts the restart.

Fix

Add an early return so a hot-update callback tolerates being invoked before buildStart, plus a regression test (handleHotUpdate-race.spec.ts) that reproduces the crash deterministically — no timing
needed — by calling handleHotUpdate on a fresh plugin instance before buildStart has run.

options.value.compiler is only assigned in the buildStart hook, not in
configResolved. Vite's restartServer() (triggered by any change to
vite.config.*) creates the new watcher and plugin instances via
_createServer(..., { listen: false }) but only calls buildStart later,
inside server.listen(). The gap between those two points — spent
awaiting server.close() of the old server — is a window in which a
file-change event can reach handleHotUpdate on a plugin instance whose
compiler is still null.

handleHotUpdate dereferenced options.value.compiler.invalidateTypeCache
unconditionally, before the file filter was even checked, so it threw

  TypeError: null is not an object
  (evaluating 'options.value.compiler.invalidateTypeCache')

for any changed file, not just .vue files, surfacing as Vite's HMR
overlay. Reproducible end-to-end with:

  touch vite.config.ts; sleep <0.1-0.8>; touch <any file>

on a project whose watched tree is large enough that server.close()
measurably outlasts the restart.

Add an early return so a hot-update callback tolerates being invoked
before buildStart, and a regression test that reproduces the crash
deterministically by calling handleHotUpdate on a fresh plugin
instance before buildStart has run.
@HeldvonKosmos HeldvonKosmos changed the title fix: avoid null-compiler crash in handleHotUpdate during vite.config restart fix: avoid null-compiler crash in handleHotUpdate during vite.config restart Aug 28, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant