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

  1. Add `export default MyReporter` at the bottom of the reporter module.
  2. 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

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


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)