{"record":{"id":"7960cf0a824dcf1c","repo":"vitest-dev/vitest","slug":"runner-must-export-a-default-function-but-got-t","errorCode":null,"errorMessage":"Runner must export a default function, but got ${typeof mod.default} imported from ${config.runner}","messagePattern":"Runner must export a default function, but got (.+?) imported from (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runners/index.ts","lineNumber":20,"sourceCode":"import type { SerializedConfig } from '../config'\nimport type { TestModuleRunner } from '../moduleRunner/testModuleRunner'\nimport type { VitestRunner, VitestRunnerConstructor } from '../runner/types'\nimport { takeCoverageInsideWorker } from '../../integrations/coverage'\nimport { rpc } from '../rpc'\nimport { loadDiffConfig, loadSnapshotSerializers } from '../setup-common'\nimport { getWorkerState } from '../utils'\nimport { TestRunner } from './test'\n\nasync function getTestRunnerConstructor(\n  config: SerializedConfig,\n  moduleRunner: TestModuleRunner,\n): Promise<VitestRunnerConstructor> {\n  if (!config.runner) {\n    return TestRunner as any as VitestRunnerConstructor\n  }\n  const mod = await moduleRunner.import(config.runner)\n  if (!mod.default && typeof mod.default !== 'function') {\n    throw new Error(\n      `Runner must export a default function, but got ${typeof mod.default} imported from ${\n        config.runner\n      }`,\n    )\n  }\n  return mod.default as VitestRunnerConstructor\n}\n\nexport async function resolveTestRunner(\n  config: SerializedConfig,\n  moduleRunner: TestModuleRunner,\n  traces: Traces,\n): Promise<VitestRunner> {\n  const TestRunner = await getTestRunnerConstructor(config, moduleRunner)\n  const testRunner = new TestRunner(config)\n\n  // inject private executor to every runner\n  Object.defineProperty(testRunner, 'moduleRunner', {","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runners/index.ts#L2-L38","documentation":"Thrown by getTestRunnerConstructor when the module referenced by the config `runner` option is imported successfully but has no usable default-exported constructor function. Vitest instantiates the custom runner via `new mod.default(config)`, so the default export must be a class or function. The message reports the actual typeof the default export for diagnosis.","triggerScenarios":"Setting `runner: './my-runner.ts'` in vitest config where that file has no default export, exports a default that is an object/value (not callable), or uses a named export instead of default.","commonSituations":"Writing a custom runner and forgetting `export default class MyRunner`; exporting the runner as a named export (`export class MyRunner`); default-exporting a configuration object instead of the class; pointing `runner` at the wrong file path.","solutions":["Add `export default` to the runner class: `export default class MyRunner extends VitestTestRunner { ... }`.","If the runner is a named export, re-export it as default: `export { MyRunner as default } from './MyRunner'`.","Verify `config.runner` points to the file that contains the class, not a config or barrel file.","Ensure the default export is a constructor (class or function), not an instance or plain object."],"exampleFix":"// before — my-runner.ts\nexport class MyRunner extends VitestTestRunner { ... }\n\n// after\nexport default class MyRunner extends VitestTestRunner { ... }","handlingStrategy":"type-guard","validationCode":"import { importMeta } from './loader' // your dynamic import helper\nasync function loadRunner(path: string) {\n  const mod = await import(/* @vite-ignore */ path)\n  if (typeof mod.default !== 'function') {\n    throw new TypeError(`Custom runner at ${path} must default-export a constructor`)\n  }\n  return mod.default\n}","typeGuard":"import type { VitestRunnerConstructor } from 'vitest/runners'\nfunction isRunnerCtor(\n  mod: any,\n): mod is VitestRunnerConstructor {\n  return typeof mod?.default === 'function'\n}","tryCatchPattern":null,"preventionTips":["Always `export default class` when authoring a custom runner.","In CI, add a smoke test that imports the runner file and asserts typeof default === 'function'.","Prefer extending VitestTestRunner so the contract is inherited."],"tags":["config","runner","custom-runner","esm"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}