vitejs/vite · error · Error
Cannot send non-custom events from the client to the server.
Error message
Cannot send non-custom events from the client to the server.
What it means
Thrown by the server module runner transport's client-side send() when the payload type is not 'custom'. The server-side ModuleRunner transport only permits custom events (user/plugin-defined) from the running module back to the dev server; built-in HMR payloads like 'update'/'connected' are server-to-client only.
Source
Thrown at packages/vite/src/node/ssr/runtime/serverModuleRunner.ts:93
}
if (typeof options.sourcemapInterceptor === 'object') {
return { ...prepareStackTrace, ...options.sourcemapInterceptor }
}
return options.sourcemapInterceptor
}
if (typeof process !== 'undefined' && 'setSourceMapsEnabled' in process) {
return 'node'
}
return prepareStackTrace
}
export const createServerModuleRunnerTransport = (options: {
channel: NormalizedServerHotChannel
}): ModuleRunnerTransport => {
const hmrClient: HotChannelClient = {
send: (payload: HotPayload) => {
if (payload.type !== 'custom') {
throw new Error(
'Cannot send non-custom events from the client to the server.',
)
}
options.channel.send(payload)
},
}
let handler: ((data: HotPayload) => void) | undefined
return {
connect({ onMessage }) {
options.channel.api!.outsideEmitter.on('send', onMessage)
options.channel.api!.innerEmitter.emit(
'vite:client:connect',
undefined,
hmrClient,
)
onMessage({ type: 'connected' })View on GitHub (pinned to b4d66fee14)
Solutions
- Use import.meta.hot.send('my-custom-event', data) — only custom event names are allowed over this transport.
- Do not attempt to emit 'update'/'connected'/'prune' payloads from the runner; those are managed by Vite itself.
- If this fires from framework code, route the message through a custom event and have the server side listen via server.hot.on(event).
Example fix
// before
hot.send({ type: 'update', updates: [...] }) // throws
// after
hot.send('my-framework-event', { payload }) Defensive patterns
Strategy: validation
Validate before calling
// only emit custom events from the runner
hot.send('my-custom-event', data) Type guard
function isCustomPayload(p: HotPayload): boolean {
return p.type === 'custom'
} Prevention
- Treat import.meta.hot.send as custom-event only; never build 'update'/'connected' payloads yourself.
- Use transport.invoke for RPC rather than hand-rolling payload types.
When it happens
Trigger: Code in an SSR module runner calling something that emits a non-custom HotPayload (e.g. import.meta.hot.send with a reserved type, or an internal HMR API misused). The guard is `if (payload.type !== 'custom')` at serverModuleRunner.ts:92.
Common situations: A plugin or framework trying to push HMR update/connected payloads from the runner to the server. Misusing import.meta.hot.send (which is meant for custom events) with a built-in payload type. Internal Vite bug where a non-custom payload is routed through this transport.
Related errors
- Cannot send non-custom events from the server to the client.
- HMR is not supported by this runner transport, but `hmr` opt
- Module "${url}" was mistakenly invalidated during fetch phas
- [module runner] Failed to load "${url}"${importer ? ` import
- [module runner] Dynamic access of "import.meta.env" is not s
AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11).
Data as JSON: /api/errors/1b46a0d06411dfce.
Report an issue: GitHub.