vitest-dev/vitest · error · Error
Mock ${id} wasn't registered. This is probably a Vitest erro
Error message
Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction. What it means
`getFactoryModule` (`nativeModuleMocker.ts:190`) is called by the native loader to resolve a manual mock's factory; it looks up the mock in the registry by id and requires it be type `'manual'`. If absent (or wrong type), it throws, explicitly noting this is likely a Vitest internal bug — the loader shouldn't be invoked for an unregistered mock. Indicates a state desync between registration and load.
Source
Thrown at packages/vitest/src/runtime/moduleRunner/nativeModuleMocker.ts:190
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>>()
private factoryPromises = new Map<string, Promise<any>>()
// potential performance improvement:
// store by URL, not ids, no need to call url.*to* methods and normalizeModuleId
public getFactoryModule(id: string): any {
const registry = this.getMockerRegistry()
const mock = registry.getById(id)
if (!mock || mock.type !== 'manual') {
throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`)
}
const mockResult = mock.resolve()
if (mockResult instanceof Promise) {
// to avoid circular dependency, we resolve this function as {__factoryPromise} in `checkCircularManualMock`
// when it's requested the second time. then the exports are exposed as `undefined`,
// but later redefined when the promise is actually resolved
const promise = createDefer()
promise.finally(() => {
this.originalModulePromises.delete(id)
})
mockResult.then(promise.resolve, promise.reject).finally(() => {
this.factoryPromises.delete(id)
})
this.factoryPromises.set(id, mockResult)
this.originalModulePromises.set(id, promise)
// Node.js on windows processes all the files first, and then runs them
// unlike Node.js logic on Mac and Unix where it also runs the code while evaluatingView on GitHub (pinned to d568f8ce37)
Solutions
- Ensure all `vi.mock(...)` calls are at the top level of the test file (they're hoisted automatically only there).
- Avoid interleaving `vi.doUnmock`/`vi.unmock` with in-flight dynamic imports of the same module.
- If the setup looks correct, file a Vitest issue with a minimal reproduction (the message itself requests this).
Defensive patterns
Strategy: try-catch
Validate before calling
// Keep all vi.mock calls hoisted at the top level of the file.
// Avoid vi.mock inside conditionals, callbacks, or dynamic branches.
vi.mock('mod', () => ({ ok: true })) // top-level, hoisted
test('uses mod', async () => { /* ... */ }) Try / catch
try {
await import('mod')
} catch (e) {
if (e instanceof Error && /Mock .* wasn't registered/i.test(e.message)) {
// file a Vitest issue; meanwhile check vi.mock hoisting/unmock ordering
} else throw e
} Prevention
- Always write vi.mock at the top level of the test file so it's hoisted.
- Don't interleave vi.doUnmock/vi.unmock with in-flight imports of the same module.
- If the message appears with correct usage, file a Vitest issue with a minimal repro.
When it happens
Trigger: Internal state desync: a `vi.doUnmock`/`vi.unmock` clearing the registry mid-load; a race in module loading; `vi.mock` calls not hoisted correctly (placed inside conditionals/callbacks) so registration is missed.
Common situations: `vi.mock` written inside an `if` or callback (not hoisted to top); mock reset/unmock interleaved with a still-pending dynamic import; a genuine Vitest bug.
Related errors
- Cannot automock '${url}' because it failed to parse.
- Failed to mock '${url}'. See the cause for more information.
- Cannot parse '${filename}' because "module.stripTypeScriptTy
- Cannot find iframe with id ${event.iframeId}
- Cannot spawn child server without a parent dev server.
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/80c52de053d71baf.json.
Report an issue: GitHub.