mergeConfig crashes with TypeError when server.ws is false and server.hmr is merged
Describe the bug
mergeConfig throws an uncaught TypeError when merging a server.hmr object while server.ws is set to false:
TypeError: Property description must be an object: undefined
This also happens during normal config resolution whenever a plugin's config/configResolved hook (or a second loadConfigFromFile result) returns an hmr object on top of a base config that has server.ws: false.
Root cause: in mergeConfigRecursively, when the recursion reaches server, setupHmrWsOptionCompat is called. When serverConfig.ws === false, that helper returns early without defining the wsOptionKeys accessors on the hmr object. Later, when recursion reaches server.hmr (base and override both have an hmr object), mergeConfigRecursively unconditionally does:
Object.defineProperty(
merged,
key,
Object.getOwnPropertyDescriptor(defaults, key)!,
)
Object.getOwnPropertyDescriptor(defaults, key) returns undefined because the accessors were never installed, and the non-null assertion turns into Object.defineProperty(merged, key, undefined) → TypeError.
I have a fix ready and will open a PR.
Reproduction
https://gist.github.com/scs0209/67329ba3aa7e6ff9e12a359e696e0336
repro.mjs:
import { mergeConfig } from 'vite'
const base = { server: { ws: false, hmr: { host: 'localhost' } } }
const override = { server: { hmr: { port: 5173 } } }
// throws:
// TypeError: Property description must be an object: undefined
console.log(mergeConfig(base, override))
Steps to reproduce
npm installa project withvite(>= 6, reproduced onv8.3.0andmain)- Run the snippet above, or start a dev server where a plugin's
confighook returns:
while another plugin'sconfig() { return { server: { ws: false } } }confighook returns{ server: { hmr: { host: '...' } } } - Observe the
TypeErrorinstead of a merged config
- Vite: reproduced on
v8.3.0and currentmain - Node.js: v26.0.0
- npm: 11.12.1
- OS: macOS (arm64)
Source: vitejs/vite