vitest-dev/vitest · error · Error
Custom reporter loaded from
Error message
Custom reporter loaded from ${path} was not the default export What it means
The custom reporter module imported successfully but has no `default` export. Vitest's loader (`loadCustomReporterModule`) specifically reads `module.default` as the reporter constructor; a named-only export is rejected.
Solutions
- Add `export default MyReporter` at the bottom of the reporter module.
- Ensure the default export is the reporter class/constructor (Vitest calls `new CustomReporter(options)`).
Example fix
// before
export class MyReporter {
onInit(ctx) {}
onTestRunEnd() { /* ... */ }
}
// after
export class MyReporter {
onInit(ctx) {}
onTestRunEnd() { /* ... */ }
}
export default MyReporter Defensive patterns
Strategy: type-guard
Type guard
import { pathToFileURL } from 'node:url'
async function isDefaultExportedReporter(path: string): Promise<boolean> {
const mod = await import(pathToFileURL(resolve(process.cwd(), path)).href)
return typeof mod.default === 'function'
} Prevention
- Always author reporter modules with `export default class MyReporter implements Reporter { ... }`.
- When migrating from CommonJS, translate `module.exports = X` to `export default X`.
When it happens
Trigger: A reporter file using `export class MyReporter {...}` or `export const MyReporter = ...` with no `export default`; the loader sees `default === null/undefined` after a successful import.
Common situations: Converting a CommonJS reporter (`module.exports = MyReporter`) to ESM and forgetting to add a default export; copying a named-export utility as a reporter.
Related errors
- Failed to load custom Reporter from
- invalid diff config file
- invalid snapshot serializer file
- Runner must export a default function, but got
- Access denied to " ". See Vite config documentation for…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/8bfec209d43e12de.
Report an issue: GitHub.
Appendix: source
Thrown at packages/vitest/src/node/reporters/utils.ts:30
): 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'],
ctx: Vitest,
): Promise<Array<Reporter | DefaultReporter | BlobReporter | DotReporter | JsonReporter | TapReporter | JUnitReporter | HangingProcessReporter | GithubActionsReporter>> {
const runner = ctx.runner
const promisedReporters = reporterReferences.map(
async (referenceOrInstance) => {
if (Array.isArray(referenceOrInstance)) {
const [reporterName, reporterOptions] = referenceOrInstance
if (reporterName === 'html') {
await ctx.packageInstaller.ensureInstalled('@vitest/ui', ctx.config.root, ctx.version)
const CustomReporter = await loadCustomReporterModule(
'@vitest/ui/reporter',View on GitHub (pinned to 1fa9837ec2)