vitest-dev/vitest · error · Error

invalid diff config file

Error message

invalid diff config file ${config.diff}. Must have a default export with config object

What it means

Thrown by loadDiffConfig when `config.diff` is a string (a file path) but the imported module's default export is not a non-null object. The diff config customizes how assertion diffs are rendered; when provided as a file path it must default-export a DiffOptions object.

Solutions

  1. Default-export a DiffOptions object: `export default { aColor: undefined, bColor: undefined }` (or your chosen keys).
  2. If you only need static options, set `diff` to an object directly in vitest config instead of a file path (loadDiffConfig returns object configs as-is).
  3. Verify the path in `config.diff` resolves to the intended file.
  4. Remove the `diff` option entirely to use defaults.

Example fix

// before — diff-config.ts
export const diff = { aAnnotation: 'expected' }

// after
export default { aAnnotation: 'expected' }
Defensive patterns

Strategy: type-guard

Validate before calling

async function loadDiff(path: string) {
  const mod = await import(/* @vite-ignore */ path)
  if (!mod?.default || typeof mod.default !== 'object') {
    throw new TypeError(`${path} must default-export a DiffOptions object`)
  }
  return mod.default
}

Type guard

function isDiffOptions(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null
}

Prevention

When it happens

Trigger: Setting `diff: './diff-config.ts'` in vitest config where that file has no default export, exports a primitive/function as default, or exports the config as a named export only.

Common situations: Authoring a diff config and forgetting `export default`; exporting a function instead of an object; typo in the file path; using a named export by habit.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/8dd63f098df194cb. Report an issue: GitHub.

Appendix: source

Thrown at packages/vitest/src/runtime/setup-common.ts:58

): Promise<SerializedDiffOptions | undefined> {
  if (typeof config.diff === 'object') {
    return config.diff
  }
  if (typeof config.diff !== 'string') {
    return
  }

  const diffModule = await moduleRunner.import(config.diff)

  if (
    diffModule
    && typeof diffModule.default === 'object'
    && diffModule.default != null
  ) {
    return diffModule.default as DiffOptions
  }
  else {
    throw new Error(
      `invalid diff config file ${config.diff}. Must have a default export with config object`,
    )
  }
}

export async function loadSnapshotSerializers(
  config: SerializedConfig,
  moduleRunner: PublicModuleRunner,
): Promise<void> {
  const files = config.snapshotSerializers

  const snapshotSerializers = await Promise.all(
    files.map(async (file) => {
      const mo = await moduleRunner.import(file)
      if (!mo || typeof mo.default !== 'object' || mo.default === null) {
        throw new Error(
          `invalid snapshot serializer file ${file}. Must export a default object`,
        )

View on GitHub (pinned to 1fa9837ec2)