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

  1. Move the async logic into a beforeAll hook inside the describe (beforeAll(async () => { await ... })).
  2. If the async work produces values needed by tests, assign them in beforeAll to variables declared in the describe scope.
  3. Keep the describe callback purely synchronous: it should only register tests and hooks, never perform setup.
  4. 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

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


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)