vitejs/vite · error · Error

HMR is not supported by this runner transport, but `hmr`…

Error message

HMR is not supported by this runner transport, but `hmr` option was set to true

What it means

When constructing a ModuleRunner with `hmr` not false, the constructor checks that `transport.connect` exists - HMR requires a duplex transport that can receive push messages. If the transport only supports invoke (request/response) and has no `connect`, the constructor throws immediately because HMR cannot function.

Solutions

  1. Set `hmr: false` in the ModuleRunner options if you do not need hot updates.
  2. Implement `connect` on your transport (a method that registers a handler and returns a disposer) to enable HMR.

Example fix

// before
new ModuleRunner({ transport: { invoke: fetchModule /* no connect */ } })
// after
new ModuleRunner({ transport: { invoke: fetchModule }, hmr: false })
Defensive patterns

Strategy: validation

Validate before calling

function validateRunnerTransportForHmr(transport: { connect?: unknown }, hmr: unknown) {
  if (hmr !== false && typeof transport.connect !== 'function') {
    throw new Error('Transport has no connect() - set hmr: false or implement connect')
  }
}

Type guard

function transportSupportsHmr(t: unknown): t is { connect: () => void; invoke: (...a: any[]) => any } {
  return !!t && typeof (t as any).connect === 'function' && typeof (t as any).invoke === 'function'
}

Prevention

When it happens

Trigger: Constructing `new ModuleRunner({ transport: invokeOnlyTransport, hmr: true })` (or leaving `hmr` at its default `true`) where `transport.connect` is undefined. Common when reusing a fetch-only transport (e.g. a manual `fetchModule` wrapper) for SSR evaluation that does not implement HMR.

Common situations: Building a custom module-runner transport for SSR/testing that omits `connect`. Forgetting to disable HMR in a one-shot evaluation script.

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/f02d1375e245935b. Report an issue: GitHub.

Appendix: source

Thrown at packages/vite/src/module-runner/runner.ts:74

    private debug?: ModuleRunnerDebugger | undefined,
  ) {
    this.evaluatedModules = options.evaluatedModules ?? new EvaluatedModules()
    this.transport = normalizeModuleRunnerTransport(options.transport)
    if (options.hmr !== false) {
      const optionsHmr = options.hmr ?? true
      const resolvedHmrLogger: HMRLogger =
        optionsHmr === true || optionsHmr.logger === undefined
          ? hmrLogger
          : optionsHmr.logger === false
            ? silentConsole
            : optionsHmr.logger
      this.hmrClient = new HMRClient(
        resolvedHmrLogger,
        this.transport,
        ({ acceptedPath }) => this.import(acceptedPath),
      )
      if (!this.transport.connect) {
        throw new Error(
          'HMR is not supported by this runner transport, but `hmr` option was set to true',
        )
      }
      this.transport.connect(createHMRHandlerForRunner(this))
    } else {
      this.transport.connect?.()
    }
    if (options.sourcemapInterceptor !== false) {
      this.resetSourceMapSupport = enableSourceMapSupport(this)
    }
  }

  /**
   * URL to execute. Accepts file path, server path or id relative to the root.
   */
  public async import<T = any>(url: string): Promise<T> {
    const fetchedModule = await this.cachedModule(url)
    return await this.cachedRequest(url, fetchedModule)

View on GitHub (pinned to b4d66fee14)