{"id":"80c52de053d71baf","repo":"vitest-dev/vitest","slug":"mock-id-wasn-t-registered-this-is-probably-a-v-80c52d","errorCode":null,"errorMessage":"Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.","messagePattern":"Mock (.+?) wasn't registered\\. This is probably a Vitest error\\. Please, open a new issue with reproduction\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/moduleRunner/nativeModuleMocker.ts","lineNumber":190,"sourceCode":"    this.processedModules.set(id, (this.processedModules.get(id) ?? 0) + 1)\n    // the module is mocked and requested a second time, let's resolve\n    // the factory function that will redefine the exports later\n    if (this.originalModulePromises.has(id)) {\n      const factoryPromise = this.factoryPromises.get(id)\n      this.originalModulePromises.get(id)?.resolve({ __factoryPromise: factoryPromise })\n    }\n  }\n\n  private originalModulePromises = new Map<string, DeferPromise<any>>()\n  private factoryPromises = new Map<string, Promise<any>>()\n\n  // potential performance improvement:\n  // store by URL, not ids, no need to call url.*to* methods and normalizeModuleId\n  public getFactoryModule(id: string): any {\n    const registry = this.getMockerRegistry()\n    const mock = registry.getById(id)\n    if (!mock || mock.type !== 'manual') {\n      throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`)\n    }\n\n    const mockResult = mock.resolve()\n    if (mockResult instanceof Promise) {\n      // to avoid circular dependency, we resolve this function as {__factoryPromise} in `checkCircularManualMock`\n      // when it's requested the second time. then the exports are exposed as `undefined`,\n      // but later redefined when the promise is actually resolved\n      const promise = createDefer()\n      promise.finally(() => {\n        this.originalModulePromises.delete(id)\n      })\n      mockResult.then(promise.resolve, promise.reject).finally(() => {\n        this.factoryPromises.delete(id)\n      })\n      this.factoryPromises.set(id, mockResult)\n      this.originalModulePromises.set(id, promise)\n      // Node.js on windows processes all the files first, and then runs them\n      // unlike Node.js logic on Mac and Unix where it also runs the code while evaluating","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/runtime/moduleRunner/nativeModuleMocker.ts#L172-L208","documentation":"`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.","triggerScenarios":"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.","commonSituations":"`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.","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)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Keep all vi.mock calls hoisted at the top level of the file.\n// Avoid vi.mock inside conditionals, callbacks, or dynamic branches.\nvi.mock('mod', () => ({ ok: true })) // top-level, hoisted\n\ntest('uses mod', async () => { /* ... */ })","typeGuard":null,"tryCatchPattern":"try {\n  await import('mod')\n} catch (e) {\n  if (e instanceof Error && /Mock .* wasn't registered/i.test(e.message)) {\n    // file a Vitest issue; meanwhile check vi.mock hoisting/unmock ordering\n  } else throw e\n}","preventionTips":["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."],"tags":["mocking","native-mocker","internal","bug-report","hoisting"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}