vitest-dev/vitest · error · Error

done() callback is deprecated, use promise instead

Error message

done() callback is deprecated, use promise instead

What it means

The TestContext object is itself callable in Vitest 4, but invoking it throws because the Mocha-style `test.cb(fn)` done-callback pattern was removed. Vitest now expects tests to signal completion via returned promises/async-await instead of a `done` argument. Calling the context as a function is the only path to this error.

Solutions

  1. Convert the test to async/await: `test('x', async () => { await something() })`.
  2. If waiting on an event, wrap it in a Promise and `await` that instead of calling done.
  3. Remove any `done` parameter from test callbacks entirely.

Example fix

// before
test('waits', (done) => {
  emitter.once('ready', done)
})

// after
test('waits', async () => {
  await once(emitter, 'ready')
})
Defensive patterns

Strategy: validation

Validate before calling

// Static: ensure no test callback declares a `done` parameter.
// grep for `test(.*,\s*\(done\)` and remove.

Prevention

When it happens

Trigger: Writing `test('x', (done) => { ...; done() })` and then invoking the injected context as the done callback; porting Mocha callback-style tests verbatim; libraries that call the first function argument they receive.

Common situations: Migrating a Mocha codebase that relied on `done` for async control flow; using event-emitter callbacks that call their last argument as completion.

Related errors


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

Appendix: source

Thrown at packages/vitest/src/runtime/runner/context.ts:163

const abortControllers = new WeakMap<TestContext, AbortController>()

export function abortIfTimeout([context]: [TestContext?, unknown?], error: Error): void {
  if (context) {
    abortContextSignal(context, error)
  }
}

export function abortContextSignal(context: TestContext, error: Error): void {
  const abortController = abortControllers.get(context)
  abortController?.abort(error)
}

export function createTestContext(
  test: Test,
  runner: VitestRunner,
): TestContext {
  const context = function () {
    throw new Error('done() callback is deprecated, use promise instead')
  } as unknown as WriteableTestContext

  let abortController = abortControllers.get(context)

  if (!abortController) {
    abortController = new AbortController()
    abortControllers.set(context, abortController)
  }

  context.signal = abortController.signal
  context.task = test

  context.skip = (condition?: boolean | string, note?: string): never => {
    if (condition === false) {
      // do nothing
      return undefined as never
    }
    test.result ??= { state: 'skip' }

View on GitHub (pinned to 1fa9837ec2)