{"id":"13d5ade8b903fc4e","repo":"vitest-dev/vitest","slug":"vitest-manual-mocks-require-a-factory-function","errorCode":null,"errorMessage":"[vitest] Manual mocks require a factory function.","messagePattern":"\\[vitest\\] Manual mocks require a factory function\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/mocker/src/registry.ts","lineNumber":108,"sourceCode":"        throw new Error(`Unknown mock type: ${(event as any).type}`)\n      }\n    }\n\n    if (typeof raw !== 'string') {\n      throw new TypeError('[vitest] Mocks require a raw string.')\n    }\n\n    if (typeof url !== 'string') {\n      throw new TypeError('[vitest] Mocks require a url string.')\n    }\n\n    if (typeof id !== 'string') {\n      throw new TypeError('[vitest] Mocks require an id string.')\n    }\n\n    if (type === 'manual') {\n      if (typeof factoryOrRedirect !== 'function') {\n        throw new TypeError('[vitest] Manual mocks require a factory function.')\n      }\n      const mock = new ManualMockedModule(raw, id, url, factoryOrRedirect)\n      this.add(mock)\n      return mock\n    }\n    else if (type === 'automock' || type === 'autospy') {\n      const mock = type === 'automock'\n        ? new AutomockedModule(raw, id, url)\n        : new AutospiedModule(raw, id, url)\n      this.add(mock)\n      return mock\n    }\n    else if (type === 'redirect') {\n      if (typeof factoryOrRedirect !== 'string') {\n        throw new TypeError('[vitest] Redirect mocks require a redirect string.')\n      }\n      const mock = new RedirectedModule(raw, id, url, factoryOrRedirect)\n      this.add(mock)","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/mocker/src/registry.ts#L90-L126","documentation":"Manual mocks are resolved lazily by invoking a factory function that returns the module's exports. Registering a `'manual'` mock therefore requires the 5th argument to be a callable factory; anything else (omitted, a string, an object) is invalid because the mock could never produce exports.","triggerScenarios":"Calling `registry.register('manual', raw, id, url)` (factory omitted) or `registry.register('manual', raw, id, url, 'notAFunction')`.","commonSituations":"Forgetting the factory; copy-pasting a redirect call into a manual call; passing an already-built object instead of a thunk.","solutions":["Provide a factory returning the module namespace: `registry.register('manual', raw, id, url, () => ({ default: stub }))`.","If you already have the exports object, wrap it in a thunk: `() => exportsObject`.","Double-check the overload — the 5th arg's type depends on `type` (function for manual, string for redirect)."],"exampleFix":"// before\nregistry.register('manual', raw, id, url)\n\n// after\nregistry.register('manual', raw, id, url, () => ({\n  default: vi.fn(),\n  helper: vi.fn(),\n}))","handlingStrategy":"type-guard","validationCode":"function registerManual(registry: any, raw: string, id: string, url: string, factory: unknown) {\n  if (typeof factory !== 'function') {\n    throw new TypeError('Manual mocks require a factory function () => module namespace.')\n  }\n  registry.register('manual', raw, id, url, factory as () => any)\n}","typeGuard":"function isFactory(v: unknown): v is () => unknown {\n  return typeof v === 'function'\n}","tryCatchPattern":null,"preventionTips":["Always type the 5th arg of a manual register call as `() => unknown`.","If you already have the exports object, wrap as `() => exportsObject`.","Keep a unit test that registers one manual mock to catch missing-factory regressions."],"tags":["mocking","registry","validation","manual-mock"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}