vitest-dev/vitest · error · Error
Failed to mock ' '. See the cause for more information.
Error message
Failed to mock '${url}'. See the cause for more information. What it means
When applying a manual mock (`vi.mock('mod', factory)`), Vitest parses the original module's source to collect its export names, then synthesizes a module shell whose exports the factory later overrides. If export collection or shell generation throws, this error wraps the cause. The catch is at nativeModuleMocker.ts:162-164.
Solutions
- Inspect `error.cause` for the underlying parse/export-collection failure.
- If the module is TypeScript, ensure Node >= 22.15 (see error 317).
- Match the factory's exports exactly to the original module's named exports (injecting new keys is unsupported per the comment at nativeModuleMocker.ts:151-152).
- If the module is non-JS (JSON, WASM, native), use `vi.mock` with `vi.hoocked` redirection or alias instead.
- Simplify the original module's export surface so the lexer can parse it.
Example fix
// before
vi.mock('./legacy-cjs', () => ({ oldApi: vi.fn() })) // lexer fails on CJS shape
// after - provide a redirect via alias in config instead
// test.server.config.ts
resolve: { alias: { './legacy-cjs': './legacy-cjs-mock.ts' } } Defensive patterns
Strategy: try-catch
Validate before calling
try { collectModuleExports(moduleId, source, format) } catch (e) { throw new Error('manual mock will fail; check cause: ' + e.message) } Type guard
null
Try / catch
try { vi.mock('./mod', factory) } catch (e) { if (/Failed to mock/.test(e.message)) { console.error(e.cause); /* alias or simplify exports */ } else throw e } Prevention
- Ensure the mocked module's named exports are statically analyzable by es/cjs-module-lexer.
- Keep mock factory exports a strict subset of the original module's exports.
- For non-JS modules, alias in Vite config instead of using `vi.mock`.
When it happens
Trigger: Calling `vi.mock('mod', () => ({ ... }))` where the original module's source cannot be parsed by `collectModuleExports` (es/cjs-module-lexer) or `createManualModuleSource`. The original error is attached as `cause`.
Common situations: Manual-mocking a TypeScript module on Node < 22.15 (no `module.stripTypeScriptTypes` — but that throws a dedicated 317 error before reaching here); module uses CJS syntax the lexer rejects; module is a JSON/native addon whose source is not JS; module has a syntax error; the mock factory is registered for a path that resolves to an unexpected file.
Related errors
- Cannot automock ' ' because it failed to parse.
- Cannot parse ' ' because "module.stripTypeScriptTypes" is…
- Cannot set serialized manual mock. Define a factory…
- vi.doMock() expects a string path, but received a
- vi.doUnmock() expects a string path, but received a
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/786227e6e0d15d20.
Report an issue: GitHub.
Appendix: source
Thrown at packages/vitest/src/runtime/moduleRunner/nativeModuleMocker.ts:163
if (transformedCode == null) {
return
}
const format = result.format?.startsWith('module') ? 'module' : 'commonjs'
try {
// we parse the module with es/cjs-module-lexer to find the original exports -- we assume the same ones are returned from the factory
// injecting new keys is not supported (and should not be advised anyway)
const exports = collectModuleExports(moduleId, transformedCode, format)
const manualMockedModule = createManualModuleSource(moduleId, exports)
return {
format: 'module',
source: manualMockedModule,
shortCircuit: true,
}
}
catch (cause) {
throw new Error(`Failed to mock '${url}'. See the cause for more information.`, { cause })
}
}
private processedModules = new Map<string, number>()
public checkCircularManualMock(url: string): void {
const filename = url.startsWith('file://') ? fileURLToPath(url) : url
const id = cleanUrl(normalizeModuleId(filename))
this.processedModules.set(id, (this.processedModules.get(id) ?? 0) + 1)
// the module is mocked and requested a second time, let's resolve
// the factory function that will redefine the exports later
if (this.originalModulePromises.has(id)) {
const factoryPromise = this.factoryPromises.get(id)
this.originalModulePromises.get(id)?.resolve({ __factoryPromise: factoryPromise })
}
}
private originalModulePromises = new Map<string, DeferPromise<any>>()View on GitHub (pinned to 1fa9837ec2)