vitest-dev/vitest · error · Error
Runner must implement "importFile" method.
Error message
Runner must implement "importFile" method.
What it means
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.
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.
Example fix
// before — custom runner missing importFile
export default class MyRunner {
constructor(config) { this.config = config }
}
// after
import { VitestTestRunner } from 'vitest/runners'
export default class MyRunner extends VitestTestRunner {
// importFile inherited; add overrides as needed
} Defensive patterns
Strategy: type-guard
Validate before calling
import type { VitestRunner } from 'vitest/runners'
function assertRunnerShape(Runner: new (c: any) => VitestRunner) {
const r = new Runner({} as any)
if (typeof r.importFile !== 'function') {
throw new TypeError('Custom runner must implement importFile(filepath, source)')
}
} Type guard
function hasImportFile(r: unknown): r is { importFile: (f: string, s: 'collect' | 'setup') => unknown } {
return typeof (r as any)?.importFile === 'function'
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Runner must export a default function, but got
- All browser instances within a project must use the same…
- "browser.instances" was set in the config, but the array is…
- Each tag defined in "test.tags" must have a "name"…
- Environment " " is not a valid environment. Path " " should…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/8360e71660121b10.
Report an issue: GitHub.
Appendix: 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 pView on GitHub (pinned to 1fa9837ec2)