vitest-dev/vitest · error · Error

Runner must implement "importFile" method.

Error message

Runner must implement "importFile" method.

What it means

Thrown by resolveTestRunner after constructing the (possibly custom) runner instance: the resulting object does not have an `importFile` method. importFile is the one mandatory method of a VitestRunner because it is how Vitest loads each test file. Without it no test can run.

Source

Thrown at packages/vitest/src/runtime/runners/index.ts:49

  moduleRunner: TestModuleRunner,
  traces: Traces,
): Promise<VitestRunner> {
  const TestRunner = await getTestRunnerConstructor(config, moduleRunner)
  const testRunner = new TestRunner(config)

  // inject private executor to every runner
  Object.defineProperty(testRunner, 'moduleRunner', {
    value: moduleRunner,
    enumerable: false,
    configurable: false,
  })

  if (!testRunner.config) {
    testRunner.config = config
  }

  if (!testRunner.importFile) {
    throw new Error('Runner must implement "importFile" method.')
  }

  if ('__setTraces' in testRunner) {
    (testRunner.__setTraces as any)(traces)
  }

  const [diffOptions] = await Promise.all([
    loadDiffConfig(config, moduleRunner),
    loadSnapshotSerializers(config, moduleRunner),
  ])
  testRunner.config._diffOptions = diffOptions

  // patch some methods, so custom runners don't need to call RPC
  const originalOnTaskUpdate = testRunner.onTaskUpdate
  testRunner.onTaskUpdate = async (task, events) => {
    const p = rpc().onTaskUpdate(task, events)
    await originalOnTaskUpdate?.call(testRunner, task, events)
    return p

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Implement `importFile(filepath: string)` on your runner class (typically delegating to a vm/vite module loader).
  2. Extend `VitestRunner` from `vitest/runners` so you inherit a default importFile and only override what you need.
  3. Confirm the method name spelling is exactly `importFile`.
  4. After build, check the compiled file exports the class with the method on its prototype.

Example fix

// before
export default class MyRunner extends VitestRunner {
  onCollected() { /* ... */ }
  // missing importFile
}

// after
export default class MyRunner extends VitestRunner {
  async importFile(file) {
    return await this.moduleRunner.import(file)
  }
}
Defensive patterns

Strategy: type-guard

Validate before calling

import MyRunner from './my-runner.js'
const proto = MyRunner.prototype
if (typeof proto.importFile !== 'function') {
  throw new Error('Custom runner is missing importFile')
}

Type guard

function hasImportFile<R extends new (...a: any[]) => any>(Runner: R): boolean {
  return typeof Runner.prototype.importFile === 'function'
}

Prevention

When it happens

Trigger: A custom runner class (config.runner) that defines other lifecycle hooks (onCollected, onAfterRunTask, etc.) but forgets importFile. A runner class whose prototype chain is broken so importFile from the base class is lost. Extending the wrong base class that does not implement importFile.

Common situations: Implementing a custom runner and overlooking the importFile requirement. Subclassing a non-runner base. Shadowing importFile with `undefined` accidentally.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/8360e71660121b10.json. Report an issue: GitHub.