vitest-dev/vitest · error · Error

Failed to load custom CoverageProviderModule from ${options.

Error message

Failed to load custom CoverageProviderModule from ${options.customProviderModule}

What it means

When provider is 'custom', Vitest dynamically imports options.customProviderModule via the runtime loader (coverage.ts:86-94). If that import rejects — module not found, syntax error, or the module throws on load — the original error is wrapped (with cause) into this message naming the configured module path.

Source

Thrown at packages/vitest/src/utils/coverage.ts:90

        ? await loader.import(builtInModule)
        : await import(/* @vite-ignore */ builtInModule)

    if (!coverageModule) {
      throw new Error(
        `Failed to load ${CoverageProviderMap[provider]}. Default export is missing.`,
      )
    }

    return coverageModule
  }

  let customProviderModule

  try {
    customProviderModule = await loader.import(options.customProviderModule!)
  }
  catch (error) {
    throw new Error(
      `Failed to load custom CoverageProviderModule from ${options.customProviderModule}`,
      { cause: error },
    )
  }

  if (customProviderModule.default == null) {
    throw new Error(
      `Custom CoverageProviderModule loaded from ${options.customProviderModule} was not the default export`,
    )
  }

  return customProviderModule.default
}

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Verify the customProviderModule path resolves from the project root (try importing it from a script).
  2. Inspect error.cause for the underlying module-load failure (missing dep, syntax error).
  3. Install any dependency the custom provider module imports.
  4. Ensure the module is valid for the current ESM/CJS context.

Example fix

// before: path typo
export default defineConfig({
  test: { coverage: { provider: 'custom', customProviderModule: './cov/provider.ts' } },
})

// after: correct existing path
export default defineConfig({
  test: { coverage: { provider: 'custom', customProviderModule: './coverage/provider.ts' } },
})
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'node:fs'
import { resolve } from 'node:path'

function assertCustomProviderPath(root: string, modulePath: string) {
  const resolved = resolve(root, modulePath)
  if (!existsSync(resolved)) {
    throw new Error(`customProviderModule not found at '${resolved}'`)
  }
}

Try / catch

try {
  await resolveCoverageProviderModule(config.coverage, loader)
} catch (e) {
  const cause = (e as Error & { cause?: Error }).cause
  console.error('Custom coverage provider failed to load:', cause?.message ?? e)
  throw e
}

Prevention

When it happens

Trigger: Configuring coverage.provider: 'custom' and coverage.customProviderModule: '<path>' where the path is wrong, the file is not valid JS/TS, or the module throws during top-level evaluation. The loader.import call rejects and is caught.

Common situations: Typo in the customProviderModule path; pointing at a file that no longer exists after a refactor; the custom provider module imports a dependency that is not installed; ESM/CJS interop error when loading the module.

Related errors


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