jestjs/jest · error · Error

Unexpected return from a matcher function. Matcher functions

Error message

Unexpected return from a matcher function.
Matcher functions should return an object in the following format:
  {message?: string | function, pass: boolean}
'${matcherUtils.stringify(result)}' was returned

What it means

_validateResult inspects the return value of every matcher (built-in and custom) and requires an object with `pass: boolean` and an optional `message` that is a string or function. If a custom matcher returns a boolean, undefined, a Promise, or an object missing `pass`, this error is thrown with the stringified result so you can see what went wrong. It protects the dispatcher from continuing on garbage.

Source

Thrown at packages/expect/src/index.ts:430

  stringMatching: stringNotMatching,
};

expect.arrayContaining = arrayContaining;
expect.arrayOf = arrayOf;
expect.closeTo = closeTo;
expect.objectContaining = objectContaining;
expect.stringContaining = stringContaining;
expect.stringMatching = stringMatching;

const _validateResult = (result: any) => {
  if (
    typeof result !== 'object' ||
    typeof result.pass !== 'boolean' ||
    (result.message &&
      typeof result.message !== 'string' &&
      typeof result.message !== 'function')
  ) {
    throw new Error(
      'Unexpected return from a matcher function.\n' +
        'Matcher functions should ' +
        'return an object in the following format:\n' +
        '  {message?: string | function, pass: boolean}\n' +
        `'${matcherUtils.stringify(result)}' was returned`,
    );
  }
};

function assertions(expected: number): void {
  const error = new ErrorWithStack(undefined, assertions);

  setState({
    expectedAssertionsNumber: expected,
    expectedAssertionsNumberError: error,
  });
}
function hasAssertions(...args: Array<unknown>): void {

View on GitHub (pinned to f49721c78e)

Solutions

  1. Return `{ pass: boolean, message: () => string }` from the custom matcher function.
  2. Use `this.isNot` to phrase the message correctly for both pass/fail branches.
  3. If you need async work, return a Promise that resolves to the result object (and ensure intermediate returns also conform).

Example fix

// before
expect.extend({
  toBeEven(received) {
    return received % 2 === 0; // wrong: boolean, not result object
  },
});

// after
expect.extend({
  toBeEven(received) {
    const pass = received % 2 === 0;
    return {
      pass,
      message: () => `expected ${received} ${this.isNot ? 'not ' : ''}to be even`,
    };
  },
});
Defensive patterns

Strategy: validation

Validate before calling

function assertMatcherResult(result: unknown): asserts result is { pass: boolean; message?: () => string } {
  if (!result || typeof result !== 'object' || typeof (result as any).pass !== 'boolean') {
    throw new Error('matcher must return { pass: boolean, message?: () => string }');
  }
}
// inside the custom matcher, before returning:
assertMatcherResult(result);
return result;

Type guard

function isMatcherResult(x: unknown): x is { pass: boolean; message?: (() => string) | string } {
  return typeof x === 'object' && x !== null && typeof (x as any).pass === 'boolean';
}

Try / catch

// _validateResult throws inside the dispatcher; fix the custom matcher's return shape rather than catching

Prevention

When it happens

Trigger: Writing a custom matcher in expect.extend that returns `true`/`false` directly, returns nothing (undefined), returns `{ message: '...' }` without `pass`, or returns a Promise (sync matchers must return the result object directly — async matchers are a separate path).

Common situations: First-time custom matcher authors forgetting the `{pass, message}` shape; refactoring a matcher and dropping the return; returning a chained ternary that evaluates to undefined; copying a matcher that threw instead of returned.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/43eaf3bcfaa74180.json. Report an issue: GitHub.