vitest-dev/vitest · error · Error

[vitest] Cannot resolve "${id}" imported from "${importer}"

Error message

[vitest] Cannot resolve "${id}" imported from "${importer}"

What it means

Thrown by importActual at packages/mocker/src/browser/mocker.ts:62-66 when this.rpc.resolveId(id, importer) returns null. resolveId delegates to Vite's resolver on the server; null means no plugin could resolve the specifier relative to the importer. This surfaces when vi.importActual or a vi.mock factory's importActual helper targets a module Vite cannot find.

Source

Thrown at packages/mocker/src/browser/mocker.ts:64

      throw new Error(`Mock ${id} wasn't resolved. This is probably a Vitest error. Please, open a new issue with reproduction.`)
    }
    return mock.cache
  }

  public async invalidate(): Promise<void> {
    const ids = Array.from(this.mockedIds)
    if (!ids.length) {
      return
    }
    await this.rpc.invalidate(ids)
    await this.interceptor.invalidate()
    this.registry.clear()
  }

  public async importActual<T>(id: string, importer: string): Promise<T> {
    const resolved = await this.rpc.resolveId(id, importer)
    if (resolved == null) {
      throw new Error(
        `[vitest] Cannot resolve "${id}" imported from "${importer}"`,
      )
    }
    const ext = extname(resolved.id)
    const url = new URL(resolved.url, this.getBaseUrl())
    const query = `_vitest_original&ext${ext}`
    const actualUrl = `${url.pathname}${
      url.search ? `${url.search}&${query}` : `?${query}`
    }${url.hash}`
    return this.wrapDynamicImport(() => import(/* @vite-ignore */ actualUrl)).then((mod) => {
      if (!resolved.optimized || typeof mod.default === 'undefined') {
        return mod
      }
      // vite injects this helper for optimized modules, so we try to follow the same behavior
      const m = mod.default
      return m?.__esModule ? m : { ...((typeof m === 'object' && !Array.isArray(m)) || typeof m === 'function' ? m : {}), default: m }
    })
  }

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Verify the specifier resolves from the importer file (check with a normal import in the same file).
  2. Install any missing dependency.
  3. Correct typos or use the alias as configured in vite.config.ts.
  4. If importing a virtual module, ensure the plugin providing it is loaded in the Vitest config.

Example fix

// before
vi.mock('./db', async () => {
  return { ...(await vi.importActual('./database')) }
})

// after
vi.mock('./db', async () => {
  return { ...(await vi.importActual<typeof import('./db')>('./db')) }
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate the specifier resolves before relying on importActual.
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
function resolvesFromImporter(spec: string, importer: string): boolean {
  try { require.resolve(spec, { paths: [importer] }); return true } catch { return false }
}

if (!resolvesFromImporter('./missing', __dirname)) {
  throw new Error(`importActual target does not resolve: ./missing`)
}

Try / catch

try {
  const actual = await vi.importActual<typeof import('./db')>('./db')
} catch (e) {
  if (e instanceof Error && /Cannot resolve/.test(e.message)) {
    console.error('vi.importActual could not resolve the module; check path/alias/deps.')
  }
  throw e
}

Prevention

When it happens

Trigger: Calling vi.importActual('./missing') or vi.importActual('uninstalled-pkg') from a test or mock factory; passing a specifier that is not resolvable from the importer file's location.

Common situations: Typo in the module path; the dependency is not installed; the path is correct only relative to a different importer; alias configured in vite.config but not applied to the resolve path used.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/13f8af0d991e6e85.json. Report an issue: GitHub.