vitest-dev/vitest · error · Error

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

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.

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)

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.