vitest-dev/vitest · error · Error

[vitest] Cannot resolve

Error message

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

What it means

Thrown by `ModuleMocker.importActual(id, importer)` in browser mode when `rpc.resolveId(id, importer)` returns null — meaning Vite's resolver could not find the module. `importActual` backs `vi.importActual` / `vi.importMock` in browser mode; a null resolution means the specifier does not resolve to any file from the given importer.

Solutions

  1. Verify the specifier resolves from the importing file with a plain `import` in a scratch file.
  2. Check `resolve.alias` in your Vitest/Vite config covers the specifier.
  3. In monorepos, build the target package or confirm the workspace symlink exists.
  4. Match the exact casing of the path on disk, especially for CI on Linux.

Example fix

// before — typo / unresolvable specifier
const actual = await vi.importActual('./utls')
// after — correct path
const actual = await vi.importActual('./utils')
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the specifier resolves before calling importActual.
// (resolveId via the Vite resolver; in user code, a try-import in a scratch file is the cheapest check.)
try {
  await import.meta.resolve(id, importer)
} catch {
  throw new Error(`importActual target does not resolve: ${id} from ${importer}`)
}

Try / catch

try {
  const actual = await vi.importActual('./mod')
} catch (err) {
  if (err instanceof Error && err.message.includes('Cannot resolve')) {
    // log, skip, or provide a fallback stub
  }
  throw err
}

Prevention

When it happens

Trigger: Calling `vi.importActual('./nonexistent')` or `vi.importActual('uninstalled-pkg')` from a test or another module. Also triggered by incorrect alias configuration, case-sensitivity mismatches on case-sensitive filesystems, or a missing file extension when the resolver cannot disambiguate.

Common situations: Typos in the module path; deleting a source file but leaving a `vi.importActual` reference; alias mismatches between `vite.config`/`vitest.config`; monorepo package not built or not linked so the specifier resolves to nothing; case-sensitive path issues when developing on macOS/Windows and running CI on Linux.

Related errors


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

Appendix: 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 1fa9837ec2)