vitejs/vite · error · Error

Vite module runner has been closed.

Error message

Vite module runner has been closed.

What it means

`getModuleInformation` is the entry to module fetching; it first checks the runner's `closed` flag and throws if `close()` was called. After close, the runner's caches are cleared and the transport is disconnected, so no further imports are valid.

Solutions

  1. Guard imports with `runner.isClosed()` before calling `import`.
  2. Ensure no concurrent work references the runner after `close()`; await all in-flight imports before closing.
  3. Create a fresh runner if you need to import again after closing.

Example fix

// before
await runner.close()
await runner.import('/src/app.ts') // throws

// after
if (!runner.isClosed()) {
  await runner.import('/src/app.ts')
}
Defensive patterns

Strategy: try-catch

Validate before calling

async function safeImport(runner: { isClosed: () => boolean; import: (u: string) => Promise<unknown> }, url: string) {
  if (runner.isClosed()) throw new Error(`Refusing to import ${url}: runner is closed`)
  return runner.import(url)
}

Type guard

function runnerIsAlive(runner: { isClosed: () => boolean }): boolean {
  return !runner.isClosed()
}

Try / catch

try {
  return await runner.import(url)
} catch (e) {
  if (e instanceof Error && /module runner has been closed/.test(e.message)) {
    // re-create runner or skip - do not retry on the closed instance
    return null
  }
  throw e
}

Prevention

When it happens

Trigger: Calling `runner.import(url)` (or any operation that triggers `cachedModule`/`getModuleInformation`) after `await runner.close()` has resolved.

Common situations: Teardown races in tests where a module import is in-flight or queued after the runner is closed. Shared runner instances being closed by one consumer while another still imports. Long-running servers that close the runner during shutdown but still service requests.

Related errors


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

Appendix: source

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

              builtin,
        )
        this.isBuiltin = createIsBuiltin(builtins)
        this.debug?.('[module runner] builtins loaded:', builtins)
      } finally {
        this.builtinsPromise = undefined
      }
    })()

    return this.builtinsPromise
  }

  private async getModuleInformation(
    url: string,
    importer: string | undefined,
    cachedModule: EvaluatedModuleNode | undefined,
  ): Promise<EvaluatedModuleNode> {
    if (this.closed) {
      throw new Error(`Vite module runner has been closed.`)
    }

    await this.ensureBuiltins()

    this.debug?.('[module runner] fetching', url)

    const isCached = !!(typeof cachedModule === 'object' && cachedModule.meta)

    const fetchedModule = // fast return for established externalized pattern
      (
        url.startsWith('data:') || this.isBuiltin?.(url)
          ? { externalize: url, type: 'builtin' }
          : await this.transport.invoke('fetchModule', [
              url,
              importer,
              {
                cached: isCached,
                startOffset: this.evaluator.startOffset,

View on GitHub (pinned to b4d66fee14)