vitest-dev/vitest · error · Error

"Istanbul" coverage provider is not compatible with "experim

Error message

"Istanbul" coverage provider is not compatible with "experimental.viteModuleRunner: false". Please, enable "viteModuleRunner" or switch to "v8" coverage provider.

What it means

Istanbul coverage instruments source by transforming modules, which requires Vitest's Vite module runner. When `experimental.viteModuleRunner` is explicitly `false`, Istanbul cannot hook module loading to inject instrumentation, so the check at resolveConfig.ts:451 forbids the combination.

Source

Thrown at packages/vitest/src/node/config/resolveConfig.ts:452

      // if `instances` were defined, but now they are empty,
      // let's throw an error because the filter is invalid
      if (!browser.instances.length) {
        throw new Error([
          `"browser.instances" was set in the config, but the array is empty. Define at least one browser config.`,
          ` The "browser.name" was set to "${browser.name}" which filtered all configs (${instances.map(c => c.browser).join(', ')}). Did you mean to use another name?`,
        ].join(''))
      }
    }

    browser.instances.forEach((instance) => {
      instance.name ??= resolved.name
        ? `${resolved.name} (${instance.browser})`
        : instance.browser
    })
  }

  if (resolved.coverage.enabled && resolved.coverage.provider === 'istanbul' && resolved.experimental?.viteModuleRunner === false) {
    throw new Error(`"Istanbul" coverage provider is not compatible with "experimental.viteModuleRunner: false". Please, enable "viteModuleRunner" or switch to "v8" coverage provider.`)
  }

  if (browser.enabled && resolved.detectAsyncLeaks) {
    logger.console.warn(c.yellow('The option "detectAsyncLeaks" is not supported in browser mode and will be ignored.'))
  }

  resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter)
  if (isAgent) {
    // default to `skipFull` and add `text-summary` reporter when `text` reporter is used on agents
    const text = resolved.coverage.reporter.find(([name]) => name === 'text')
    const textSummary = resolved.coverage.reporter.find(([name]) => name === 'text-summary')
    if (text) {
      (text as any)[1] = { skipFull: true, ...text[1] as any }
      if (!textSummary) {
        resolved.coverage.reporter.push(['text-summary', {}])
      }
    }
  }

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Remove the `experimental.viteModuleRunner: false` override so Istanbul can instrument modules.
  2. Switch coverage to the v8 provider, which does not require the module runner: `coverage: { provider: 'v8' }`.
  3. If you must keep viteModuleRunner off, disable coverage entirely.

Example fix

// before
export default defineConfig({ test: { coverage: { enabled: true, provider: 'istanbul' }, experimental: { viteModuleRunner: false } } })
// after
export default defineConfig({ test: { coverage: { enabled: true, provider: 'v8' } } })
Defensive patterns

Strategy: type-guard

Validate before calling

function assertCoverageCompatible(config: any) {
  if (config?.coverage?.enabled && config.coverage.provider === 'istanbul' && config.experimental?.viteModuleRunner === false) {
    throw new Error('Istanbul coverage requires viteModuleRunner; switch to v8 or remove the experimental flag.')
  }
}

Type guard

function isCoverageConfigValid(config: any): boolean {
  return !(config?.coverage?.enabled && config.coverage.provider === 'istanbul' && config.experimental?.viteModuleRunner === false)
}

Prevention

When it happens

Trigger: Set `coverage: { enabled: true, provider: 'istanbul' }` together with `experimental: { viteModuleRunner: false }`.

Common situations: Opting out of the Vite module runner for performance/compat reasons while still wanting Istanbul coverage; copying an experimental flag from a workaround.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/d8004f0d5e1199a1.json. Report an issue: GitHub.