vitest-dev/vitest · error · Error
Failed to load custom Reporter from
Error message
Failed to load custom Reporter from ${path} What it means
Vitest tried to dynamically import the custom reporter module at `path` and the import itself threw (module not found, syntax error, top-level throw, unsupported ESM/CJS). The original error is attached as `cause` so the real reason is preserved; this outer message only identifies which reporter path failed.
Solutions
- Inspect `error.cause` for the underlying module error (the real message).
- Confirm the path resolves from the project root (Vitest resolves it via the module runner).
- Reproduce the import standalone: `node -e "import('./path').then(console.log).catch(console.error)"`.
- Fix the missing dependency / syntax error / interop issue inside the reporter module.
Example fix
// inspecting the cause
try {
await startVitest('test', [], {}, { reporters: [['./my-reporter.js', {}]] })
} catch (e) {
console.error(e.message) // 'Failed to load custom Reporter from ./my-reporter.js'
console.error(e.cause) // real error, e.g. Cannot find module 'pino'
} Defensive patterns
Strategy: try-catch
Validate before calling
import { pathToFileURL } from 'node:url'
// Pre-validate that the reporter module is importable from the project root.
async function assertReporterImportable(path: string) {
await import(pathToFileURL(resolve(process.cwd(), path)).href)
} Try / catch
try {
await startVitest('test', [], {}, { reporters: [[reporterPath, {}]] })
} catch (e) {
if (e instanceof Error && e.message.startsWith('Failed to load custom Reporter from ')) {
console.error('Underlying import error:', e.cause)
// fix the module, then retry
}
throw e
} Prevention
- Resolve reporter paths from the project root; prefer absolute paths or package specifiers.
- Test that `import(reporterPath)` works in isolation before wiring it into Vitest.
When it happens
Trigger: `reporters: [['./wrong-path.js', {}]]` or any non-builtin reporter name that resolves to a module that errors on import; `loadCustomReporterModule` catches the import rejection and re-throws wrapped.
Common situations: Typo in the reporter path; missing dependency imported by the reporter; ESM/CJS interop failure; reporter file has a syntax error or throws at module top level.
Related errors
- Custom reporter loaded from
- Access denied to " ". See Vite config documentation for…
- All browser instances within a project must use the same…
- Blob reporter is not supported in watch mode
- "browser.instances" was set in the config, but the array is…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/c48cce2c2810e60b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/vitest/src/node/reporters/utils.ts:18
import type { ModuleRunner } from 'vite/module-runner'
import type { Vitest } from '../core'
import type { ResolvedConfig } from '../types/config'
import type { Reporter } from '../types/reporter'
import type { BlobReporter } from './blob'
import type { BuiltinReporters, DefaultReporter, DotReporter, GithubActionsReporter, HangingProcessReporter, JsonReporter, JUnitReporter, TapReporter } from './index'
import { ReportersMap } from './index'
async function loadCustomReporterModule<C extends Reporter>(
path: string,
runner: ModuleRunner,
): Promise<new (options?: unknown) => C> {
let customReporterModule: { default: new () => C }
try {
customReporterModule = await runner.import(path)
}
catch (customReporterModuleError) {
throw new Error(`Failed to load custom Reporter from ${path}`, {
cause: customReporterModuleError as Error,
})
}
if (
customReporterModule.default === null
|| customReporterModule.default === undefined
) {
throw new Error(
`Custom reporter loaded from ${path} was not the default export`,
)
}
return customReporterModule.default
}
function createReporters(
reporterReferences: ResolvedConfig['reporters'],View on GitHub (pinned to 1fa9837ec2)