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-circus throws this when a describe() callback returns a Promise. describe blocks run synchronously during test collection (the tree is built before any test executes), so an async describe would mean the test tree is incomplete when Jest tries to run it. The guard at packages/jest-circus/src/index.ts:78 calls isPromise() on the callback's return value and throws an ErrorWithStack pointing at the describe function so the stack trace lands on the user's call site.
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 f49721c78e)
Solutions
- Remove the `async` keyword from the describe callback and move the async setup into `beforeAll(async () => { ... })`; beforeAll/afterAll/test bodies ARE allowed to be async.
- If you are returning a value unintentionally (e.g. an arrow shorthand `describe('x', () => setup())`), change to a block body `describe('x', () => { setup(); })` so nothing is returned.
- Enable the `eslint-plugin-jest` rule `no-return-promise-in-describe` to catch this at lint time.
Example fix
// before
describe('users', async () => {
const db = await connectDb();
it('lists users', () => expect(db.list()).resolves.toEqual([]));
});
// after
describe('users', () => {
let db;
beforeAll(async () => { db = await connectDb(); });
it('lists users', () => expect(db.list()).resolves.toEqual([]));
}); Defensive patterns
Strategy: validation
Validate before calling
// Before authoring, assert the describe callback is a non-async function.
function assertSyncDescribe(fn: () => void): void {
if (fn.constructor?.name === 'AsyncFunction') {
throw new Error('describe callback must not be async; move async work into beforeAll');
}
}
// usage: assertSyncDescribe(() => { /* body */ }); describe('x', () => { /* body */ }); Type guard
const isPromise = (v: unknown): v is Promise<unknown> => v != null && typeof (v as any).then === 'function';
Prevention
- Never mark a describe callback `async`; use beforeAll/beforeEach for async setup.
- Use eslint-plugin-jest rule `no-return-promise-in-describe`.
- Prefer block bodies for describe so no value is accidentally returned.
When it happens
Trigger: Writing `describe('x', async () => { await db.connect(); it(...) })`, awaiting a top-level promise inside a describe body, or returning a thenable from describe (e.g. `describe('x', () => someAsyncFn())`). The check fires the moment the describe callback returns a thenable.
Common situations: Developers new to Jest assume describe is async-aware and try to do shared DB/container setup at the top of describe instead of in a hook. Migration from Mocha (which permits async describe in some setups) also surfaces this.
Related errors
- A "describe" callback must not return a value.
- Todo must be called with only a description.
- received value must be a promise or a function returning a p
- received value must be a number
- received value must be a non-null object
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/a23593129b58c08e.json.
Report an issue: GitHub.