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 test context object is itself a callable function in Vitest. Vitest intentionally throws this error when the context is invoked directly, because the old Node.js/Tapas-style `done()` callback pattern (where you call `done()` to signal test completion) is deprecated. Vitest expects tests to return a Promise or be async instead of calling a completion callback. The error is thrown at context construction time in `createTestContext` (context.ts:162-164).
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 d568f8ce37)
Solutions
- Convert the test to async/await: `test('x', async () => { await doWork(); })` and remove all `done()` calls.
- If `done` is passed to a callback-based API, wrap it: `await new Promise(resolve => apiCall(resolve))` instead of calling `done`.
- Ensure no code treats the `TestContext` object as a callable function.
Example fix
// before
test('loads data', (done) => {
fetchData((err, data) => {
expect(data).toBeTruthy()
done()
})
})
// after
test('loads data', async () => {
const data = await new Promise((resolve, reject) =>
fetchData((err, d) => err ? reject(err) : resolve(d))
)
expect(data).toBeTruthy()
}) Defensive patterns
Strategy: validation
Validate before calling
// Ensure test functions are async and never invoke the context as a callable.
// Lint rule: disallow a parameter named 'done' in test callbacks.
// eslintrc example: { 'rules': { 'no-restricted-syntax': ['error', { 'selector': "CallExpression[callee.name='test'][arguments.0.type!='ArrowFunction']", 'message': 'Use async tests, not done callbacks' }] } } Prevention
- Always write test callbacks as `async () => {}`.
- Never name a test parameter `done`.
- When wrapping callback APIs, promisify them instead of using done.
When it happens
Trigger: Writing a test as `test('x', (done) => { doWork(); done(); })` and then invoking the context argument as a function; calling the `TestContext` object directly, e.g. `ctx()` inside a fixture or custom matcher that received the context.
Common situations: Migrating from Mocha/Jasmine/Jest done-callback tests to Vitest; third-party libraries that call a context-like argument as a function; legacy test code using `done`-callback style.
Related errors
- Cannot take a screenshot without a test path
- stopChunkTrace cannot be called outside of the test file.
- This command can only be called inside a test file.
- Cannot upload files outside of a test
- Cannot take a screenshot outside of a test.
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/f2bf65cbb5d6727e.json.
Report an issue: GitHub.