{"record":{"id":"8360e71660121b10","repo":"vitest-dev/vitest","slug":"runner-must-implement-importfile-method","errorCode":null,"errorMessage":"Runner must implement \"importFile\" method.","messagePattern":"Runner must implement \"importFile\" method\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runners/index.ts","lineNumber":49,"sourceCode":"  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', {\n    value: moduleRunner,\n    enumerable: false,\n    configurable: false,\n  })\n\n  if (!testRunner.config) {\n    testRunner.config = config\n  }\n\n  if (!testRunner.importFile) {\n    throw new Error('Runner must implement \"importFile\" method.')\n  }\n\n  if ('__setTraces' in testRunner) {\n    (testRunner.__setTraces as any)(traces)\n  }\n\n  const [diffOptions] = await Promise.all([\n    loadDiffConfig(config, moduleRunner),\n    loadSnapshotSerializers(config, moduleRunner),\n  ])\n  testRunner.config._diffOptions = diffOptions\n\n  // patch some methods, so custom runners don't need to call RPC\n  const originalOnTaskUpdate = testRunner.onTaskUpdate\n  testRunner.onTaskUpdate = async (task, events) => {\n    const p = rpc().onTaskUpdate(task, events)\n    await originalOnTaskUpdate?.call(testRunner, task, events)\n    return p","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runners/index.ts#L31-L67","documentation":"Thrown by resolveTestRunner after instantiating the (custom or built-in) runner class if the instance has no `importFile` method. importFile is the only strictly required method of the VitestRunner contract — it is how test and setup files are loaded for both collection and execution — so a runner lacking it cannot function.","triggerScenarios":"A custom runner class (set via `config.runner`) that extends VitestTestRunner incorrectly, overrides/omits importFile, or does not extend the base runner and forgets to define importFile. The built-in TestRunner always provides it, so this is only hit with custom runners.","commonSituations":"Implementing a runner from scratch (not extending VitestTestRunner) and missing importFile; accidentally renaming or deleting importFile while overriding other methods; a runner written against an older Vitest API.","solutions":["Extend the base runner so importFile is inherited: `export default class MyRunner extends VitestTestRunner { ... }`.","If implementing from scratch, define `importFile(filepath: string, source: 'collect' | 'setup'): unknown` per the VitestRunner interface (types.ts:1733).","Check you did not shadow or delete importFile in a subclass."],"exampleFix":"// before — custom runner missing importFile\nexport default class MyRunner {\n  constructor(config) { this.config = config }\n}\n\n// after\nimport { VitestTestRunner } from 'vitest/runners'\nexport default class MyRunner extends VitestTestRunner {\n  // importFile inherited; add overrides as needed\n}","handlingStrategy":"type-guard","validationCode":"import type { VitestRunner } from 'vitest/runners'\nfunction assertRunnerShape(Runner: new (c: any) => VitestRunner) {\n  const r = new Runner({} as any)\n  if (typeof r.importFile !== 'function') {\n    throw new TypeError('Custom runner must implement importFile(filepath, source)')\n  }\n}","typeGuard":"function hasImportFile(r: unknown): r is { importFile: (f: string, s: 'collect' | 'setup') => unknown } {\n  return typeof (r as any)?.importFile === 'function'\n}","tryCatchPattern":null,"preventionTips":["Extend VitestTestRunner instead of implementing the interface from scratch.","Add a type test (`expectTypeOf(MyRunner).toMatchTypeOf<VitestRunner>()`) to catch regressions.","Never delete or rename importFile when overriding other methods."],"tags":["config","runner","custom-runner","validation"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}