jestjs/jest · error · ErrorWithStack
Returning a Promise from "describe" is not supported. Tests…
Error message
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
What it means
Jest's test registry is built synchronously: when you call describe(), jest-circus immediately executes your callback (index.ts:76) to collect child test/hook registrations into its event tree. If that callback returns a Promise, the registry would be incomplete at dispatch time, so _dispatchDescribe rejects it outright (index.ts:78-82). Async setup work belongs in beforeAll/beforeEach, which DO run asynchronously during the later run() phase.
Solutions
- Move the async logic into a beforeAll hook inside the describe (beforeAll(async () => { await ... })).
- If the async work produces values needed by tests, assign them in beforeAll to variables declared in the describe scope.
- Keep the describe callback purely synchronous: it should only register tests and hooks, never perform setup.
- For data-driven async setup, use beforeAll with jest.each or compute the data synchronously.
Example fix
// before
describe('users', async () => {
const user = await createUser();
test('has name', () => expect(user.name).toBe('a'));
});
// after
describe('users', () => {
let user: User;
beforeAll(async () => {
user = await createUser();
});
test('has name', () => expect(user.name).toBe('a'));
}); Defensive patterns
Strategy: validation
Validate before calling
// Before writing a describe, enforce that its callback is not async.
function assertSyncDescribe(fn: (...args: any[]) => any): void {
if (fn.constructor.name === 'AsyncFunction') {
throw new Error('describe callback must not be async; use beforeAll for async setup');
}
}
// usage:
const cb = () => { test('x', () => {}); };
assertSyncDescribe(cb);
describe('suite', cb); Type guard
function isSyncDescribeFn(fn: unknown): fn is () => void {
return typeof fn === 'function' && fn.constructor.name !== 'AsyncFunction';
} Prevention
- Enable an eslint rule (e.g. jest/no-async-describe community rule) to reject async describe callbacks at lint time.
- Keep describe bodies to registration only; do all async work in beforeAll/beforeEach.
- In code review, flag any 'async' keyword on a describe callback.
When it happens
Trigger: Calling describe('suite', async () => {...}), or returning any thenable from the describe callback (e.g. describe('x', () => someAsyncFn())), or wrapping the body in an async IIFE. The check uses isPromise() on the return value of blockFn() at index.ts:76-78.
Common situations: Migrating from Mocha where describe callbacks could be async; refactoring setup code and accidentally marking the describe arrow function async; calling an async helper at the top level of describe instead of inside beforeAll.
Related errors
- A "describe" callback must not return a value.
- describe does not expect any arguments
- Invalid second argument
- Jest: concurrent test
- Jest: `failing` tests are only supported in `jest-circus`.
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/a23593129b58c08e.
Report an issue: GitHub.
Appendix: source
Thrown at packages/jest-circus/src/index.ts:79
throw asyncError;
}
try {
blockName = convertDescriptorToString(blockName);
} catch (error) {
asyncError.message = (error as Error).message;
throw asyncError;
}
dispatchSync({
asyncError,
blockName,
mode,
name: 'start_describe_definition',
});
const describeReturn = blockFn();
if (isPromise(describeReturn)) {
throw new ErrorWithStack(
'Returning a Promise from "describe" is not supported. Tests must be defined synchronously.',
describeFn,
);
} else if (describeReturn !== undefined) {
throw new ErrorWithStack(
'A "describe" callback must not return a value.',
describeFn,
);
}
dispatchSync({blockName, mode, name: 'finish_describe_definition'});
};
const _addHook = (
fn: Circus.HookFn,
hookType: Circus.HookType,
hookFn: THook,
timeout?: number,View on GitHub (pinned to 8e6d128e4a)