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

  1. Inspect `error.cause` for the underlying parse/export-collection failure.
  2. If the module is TypeScript, ensure Node >= 22.15 (see error 317).
  3. 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).
  4. If the module is non-JS (JSON, WASM, native), use `vi.mock` with `vi.hoocked` redirection or alias instead.
  5. 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

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


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)