vitest-dev/vitest · error · Error

Cannot import " ": its vm context was torn down.

Error message

Cannot import "${mod.identifier}": its vm context was torn down.

What it means

Thrown by getContextExecutor when a module's vm context no longer carries the VITEST_VM_CONTEXT_SYMBOL property, meaning the context was disposed (torn down) while a dynamic import or import.meta.resolve is still being serviced against a module that lived in it. Vitest associates each test file's vm world with a context; once that file's run ends the context is torn down and any lingering reference fails fast.

Solutions

  1. Ensure async work that performs dynamic imports completes (or is cancelled) before the test file finishes — await it in the test or clean it up in afterAll.
  2. Avoid caching modules or contexts across file boundaries; let each file's vm world be self-contained.
  3. If using workers/subprocesses, terminate them in teardown so they cannot issue imports afterward.
  4. Treat this as a symptom of a lifecycle leak: find what holds the module reference past teardown.

Example fix

// before — dynamic import resolves after teardown
let mod
beforeAll(() => {
  setTimeout(() => { mod = await import('./late') }, 10000)
})

// after — await inside the test lifecycle
beforeAll(async () => {
  mod = await import('./late')
})
Defensive patterns

Strategy: validation

Validate before calling

// Ensure async work that imports modules completes before file teardown
import { beforeEach, afterEach } from 'vitest'
let pending: Promise<unknown> | null = null
beforeEach(() => { pending = null })
afterEach(async () => { if (pending) await pending })
function track(p: Promise<unknown>) { pending = p }

Try / catch

try {
  await import(/* @vite-ignore */ specifier)
} catch (e) {
  if (String(e?.message).includes('its vm context was torn down')) {
    // the originating file's vm world is gone; abandon the import
    return
  }
  throw e
}

Prevention

When it happens

Trigger: An asynchronous import() or `import.meta.resolve` triggered by a module in a finished test file resolves after the file's vm context has been disposed — e.g. a pending dynamic import or worker callback that fires during/after teardown. Reached via staticImportModuleDynamically and staticInitializeImportMeta's meta.resolve.

Common situations: Long-running async work (timers, workers, fetch) started in one test file that tries to dynamically import code after the file's run completed; sharing a module reference across files in a way that outlives its vm context; race conditions during fast bail/cancel where contexts are torn down while imports are mid-flight.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/430744304fd6b009. Report an issue: GitHub.

Appendix: source

Thrown at packages/vitest/src/runtime/vm/esm-executor.ts:67

    }
    return { mime, code: Buffer.from(code, 'base64') }
  }
  if (!encoding || encoding === 'charset=utf-8') {
    code = decodeURIComponent(code)
  }
  else if (encoding === 'base64') {
    code = Buffer.from(code, 'base64').toString()
  }
  else {
    throw new Error(`Invalid data URI encoding: ${encoding}`)
  }
  return { mime, code }
}

function getContextExecutor(mod: VMModule): ExternalModulesExecutor {
  const vmContext = (mod.context as any)?.[VITEST_VM_CONTEXT_SYMBOL]
  if (!vmContext) {
    throw new Error(`Cannot import "${mod.identifier}": its vm context was torn down.`)
  }
  return vmContext.externalModulesExecutor
}

async function staticImportModuleDynamically(specifier: string, referencer: VMModule): Promise<VMModule> {
  return getContextExecutor(referencer).importModuleDynamically(specifier, referencer)
}

function staticInitializeImportMeta(meta: ImportMeta, mod: VMModule): void {
  meta.url = mod.identifier
  if (mod.identifier.startsWith('file:')) {
    const filename = fileURLToPath(mod.identifier)
    meta.filename = filename
    meta.dirname = dirname(filename)
  }
  meta.resolve = (specifier: string, importer?: string | URL) => {
    return getContextExecutor(mod).resolve(
      specifier,

View on GitHub (pinned to 1fa9837ec2)