jestjs/jest · error · Error

Jest: concurrent test "${spec.getFullName()}" must return a

Error message

Jest: concurrent test "${spec.getFullName()}" must return a Promise.

What it means

Thrown by the concurrent test wrapper in jest-jasmine2 (jasmineAsyncInstall.ts:220) when the body of a concurrent test (it.concurrent / test.concurrent) does not return a Promise. Concurrent tests run through a mutex that awaits the returned value, so a synchronous return is treated as a programming error.

Source

Thrown at packages/jest-jasmine2/src/jasmineAsyncInstall.ts:220

  const concurrentFn = function (
    specName: Global.TestNameLike,
    fn: Global.ConcurrentTestFn,
    timeout?: number,
  ) {
    let promise: Promise<unknown> = Promise.resolve();

    const spec = originalFn.call(env, specName, () => promise, timeout);
    if (env != null && !env.specFilter(spec)) {
      return spec;
    }

    try {
      promise = mutex(() => {
        const promise = fn();
        if (isPromise(promise)) {
          return promise;
        }
        throw new Error(
          `Jest: concurrent test "${spec.getFullName()}" must return a Promise.`,
        );
      });
    } catch (error) {
      promise = Promise.reject(error);
    }
    // Avoid triggering the uncaught promise rejection handler in case the test errors before
    // being awaited on.
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    promise.catch(() => {});

    return spec;
  };

  // eslint-disable-next-line unicorn/consistent-function-scoping
  const failing = () => {
    throw new Error(
      'Jest: `failing` tests are only supported in `jest-circus`.',

View on GitHub (pinned to 8e6d128e4a)

Solutions

  1. Make the body async and return/await: test.concurrent('x', async () => { await doAsyncWork(); }).
  2. If the work is genuinely synchronous, use plain test()/it() instead of test.concurrent().
  3. Replace jest done-callback style with promise-returning async functions when using concurrent.
  4. Add a lint rule banning test.concurrent bodies that are not async functions.

Example fix

// before
test.concurrent('sync', () => { compute(); });
// after
test.concurrent('async', async () => { await computeAsync(); });
Defensive patterns

Strategy: type-guard

Validate before calling

const body = () => doWork();
if (!body.toString().includes('async') && typeof body() !== 'object') {
  // sanity check; better to just always use async
}
test.concurrent('x', async () => { await doWork(); });

Type guard

const returnsPromise = (fn: (...a: any[]) => unknown): fn is (...a: any[]) => Promise<unknown> =>
  fn.constructor.name === 'AsyncFunction';

Prevention

When it happens

Trigger: test.concurrent('x', () => { doSyncWork(); }) with no async/return, test.concurrent('x', (done) => { done(); }) using the legacy done callback instead of returning a promise, or test.concurrent('x', () => someSyncResult) where the function returns a plain value.

Common situations: Converting a sync test to concurrent without making it async; mixing jest done-style callbacks with concurrent mode; returning undefined from an arrow function whose body is a block; bumping Jest versions where concurrent semantics tightened.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/c2c4ec58586aa014. Report an issue: GitHub.