vitejs/vite · error · Error

fetchModule is disabled in this environment

Error message

fetchModule is disabled in this environment

What it means

The hot channel's invoke handler for fetchModule at packages/vite/src/node/server/environment.ts:161 throws when context.disableFetchModule is true. Environments that opt out of module fetching over the hot channel (e.g. remote runner transports, some bundled-dev setups) set this flag; any caller that still issues a fetchModule invoke gets rejected.

Source

Thrown at packages/vite/src/node/server/environment.ts:161

    this._crawlEndFinder = setupOnCrawlEnd()

    this._remoteRunnerOptions = context.remoteRunner ?? {}
    this._skipFsCheck = !!(
      context.transport &&
      !(isWebSocketServer in context.transport) &&
      context.transport.skipFsCheck
    )

    this.hot = context.transport
      ? isWebSocketServer in context.transport
        ? context.transport
        : normalizeHotChannel(context.transport, context.hot)
      : normalizeHotChannel({}, context.hot)

    this.hot.setInvokeHandler({
      fetchModule: (id, importer, options) => {
        if (context.disableFetchModule) {
          throw new Error('fetchModule is disabled in this environment')
        }
        return this.fetchModule(id, importer, options)
      },
      getBuiltins: async () => {
        return this.config.resolve.builtins.map((builtin) =>
          typeof builtin === 'string'
            ? { type: 'string', value: builtin }
            : { type: 'RegExp', source: builtin.source, flags: builtin.flags },
        )
      },
    })

    this.hot.on(
      'vite:invalidate',
      ({ path, message, firstInvalidatedBy }, client) => {
        this.invalidateModule(
          {
            path,

View on GitHub (pinned to 89620f09af)

Solutions

  1. Do not call fetchModule on environments where it is disabled — fetch modules through the path the environment intends (e.g. its remoteRunner or transport).
  2. When constructing the environment, leave disableFetchModule unset (default) if you need module fetching.
  3. Guard callers with environment.runner / transport feature detection before issuing fetchModule.

Example fix

// before
await environment.hot.invoke('fetchModule', id)

// after
if (!context.disableFetchModule) {
  await environment.hot.invoke('fetchModule', id)
} else {
  // use the environment's native module loader
}
Defensive patterns

Strategy: type-guard

Validate before calling

function canFetchModule(ctx: { disableFetchModule?: boolean }): boolean {
  return !ctx.disableFetchModule
}
if (canFetchModule(envContext)) {
  await environment.hot.invoke('fetchModule', id, importer)
}

Type guard

function fetchModuleEnabled(env: { hot: { invoke: (m: string) => unknown } } & { disableFetchModule?: boolean }): boolean {
  return !(env as any).disableFetchModule
}

Try / catch

try {
  await env.hot.invoke('fetchModule', id)
} catch (e) {
  if (/fetchModule is disabled/.test((e as Error).message)) {
    // load the module through the environment's native runner instead
  } else throw e
}

Prevention

When it happens

Trigger: Calling environment.hot.send / invoke('fetchModule', ...) (directly or via server.transport) on an environment whose context.disableFetchModule = true; a plugin or framework runtime that always calls fetchModule without checking support; mismatched server/client expectations where the client expects to fetch modules but the environment disabled it.

Common situations: Custom/remote environments that resolve modules themselves; bundled-dev mode where deps optimizer is disabled; frameworks (RSC, SSR runners) that ship their own module loading and turn fetchModule off.

Related errors


AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03). Data as JSON: /data/errors/621e03027622ea77.json. Report an issue: GitHub.