vitest-dev/vitest · error · Error

Vitest Module Runner doesn't support Vite HMR events.

Error message

Vitest Module Runner doesn't support Vite HMR events.

What it means

ServerModuleRunner is constructed with hmr:false and its transport.invoke (serverRunner.ts:22) only handles 'custom' events; any other Vite HMR event type (full-reload, update, etc.) is rejected. Vitest's runner intentionally doesn't participate in Vite's HMR — it runs modules on demand.

Source

Thrown at packages/vitest/src/node/environments/serverRunner.ts:22

import { readFile } from 'node:fs/promises'
import { isRunnableDevEnvironment } from 'vite'
import { ModuleRunner } from 'vite/module-runner'
import { VitestModuleEvaluator } from '#module-evaluator'
import { normalizeResolvedIdToUrl } from './normalizeUrl'

export class ServerModuleRunner extends ModuleRunner {
  constructor(
    private environment: DevEnvironment,
    fetcher: VitestFetchFunction,
    private config: ResolvedConfig,
  ) {
    super(
      {
        hmr: false,
        transport: {
          async invoke(event) {
            if (event.type !== 'custom') {
              throw new Error(`Vitest Module Runner doesn't support Vite HMR events.`)
            }
            const { name, data } = event.data
            if (name === 'getBuiltins') {
              return await environment.hot.handleInvoke(event)
            }
            if (name !== 'fetchModule') {
              return { error: new Error(`Unknown method: ${name}. Expected "fetchModule".`) }
            }
            try {
              const result = await fetcher(data[0], data[1], environment, false, data[2])
              if ('tmp' in result) {
                const code = await readFile(result.tmp)
                return { result: { ...result, code } }
              }
              return { result }
            }
            catch (error) {
              return { error }

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Don't route HMR events through the vitest runner transport — only send 'custom' events if you must use it.
  2. Disable/skip HMR-emitting plugin logic under vitest (guard on process.env.VITEST or command === 'serve' && !import.meta.env.VITEST).
  3. Use the regular environment.hot channel for vitest-aware messaging instead of HMR.

Example fix

// before (vite plugin)
server.ws.send({ type: 'full-reload' })

// after
if (!process.env.VITEST) server.ws.send({ type: 'full-reload' })
Defensive patterns

Strategy: validation

Validate before calling

transport: {
  async invoke(event) {
    if (event.type !== 'custom') return { error: new Error('HMR not supported') }
    // ...
  }
}

Type guard

function isCustomEvent(event: any): boolean {
  return event?.type === 'custom'
}

Prevention

When it happens

Trigger: A Vite plugin, custom transport, or HMR client pushing a non-custom event (e.g. server.ws.send({ type: 'full-reload' })) into the vitest module-runner transport. Should never occur in standard vitest flows.

Common situations: A custom Vite plugin firing HMR events during vitest runs; a misconfigured environment that wires HMR into the runner transport; experimental plugin development.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/815f430ba07f8904.json. Report an issue: GitHub.