vitest-dev/vitest · error · JestExtendError

(customMessage ? `${customMessage}: ` : '') + message()

Error message

(customMessage ? `${customMessage}: ` : '') + message()

What it means

Thrown by `jest.extend`-registered matchers when the matcher returns `pass: false` (or `pass: true` under `.not`). It builds the user-facing error by prepending any custom message and wrapping it in `JestExtendError` along with actual/expected/meta. This is the standard failure signal of a custom matcher.

Source

Thrown at packages/expect/src/jest-extend.ts:125

            const thenable = result as PromiseLike<SyncExpectationResult>
            return thenable.then(({ pass, message, actual, expected, meta }) => {
              if ((pass && isNot) || (!pass && !isNot)) {
                const errorMessage = (customMessage ? `${customMessage}: ` : '') + message()
                throw new JestExtendError(
                  errorMessage,
                  actual,
                  expected,
                  { assertionName: expectAssertionName, meta },
                )
              }
            })
          }

          const { pass, message, actual, expected, meta } = result as SyncExpectationResult

          if ((pass && isNot) || (!pass && !isNot)) {
            const errorMessage = (customMessage ? `${customMessage}: ` : '') + message()
            throw new JestExtendError(
              errorMessage,
              actual,
              expected,
              { assertionName: expectAssertionName, meta },
            )
          }
        }

        const softWrapper = wrapAssertion(utils, expectAssertionName, __VITEST_EXTEND_ASSERTION__)
        utils.addMethod(
          (globalThis as any)[JEST_MATCHERS_OBJECT].matchers,
          expectAssertionName,
          softWrapper,
        )
        utils.addMethod(
          c.Assertion.prototype,
          expectAssertionName,
          softWrapper,

View on GitHub (pinned to 1fa9837ec2)

Solutions

  1. Treat as a normal assertion failure: inspect actual vs expected in the message and fix the SUT or the test.
  2. If the matcher itself is wrong, correct its `pass`/`message` in the `expect.extend` definition.
  3. Add a custom message via `expect(value, 'context').myMatcher(...)` to clarify failures.

Example fix

// before: custom matcher reports failure
expect.extend({
  toBeEven(received) {
    return { pass: received % 2 === 0, message: () => 'expected even' }
  },
})
expect(3).toBeEven() // throws JestExtendError

// after
expect(4).toBeEven()
Defensive patterns

Strategy: try-catch

Validate before calling

try {
  expect(value).myCustomMatcher(arg)
} catch (e) {
  // JestExtendError: inspect actual vs expected and adjust SUT or matcher
}

Try / catch

try {
  expect(value, 'context').myMatcher(arg)
} catch (err) {
  if (!(err instanceof Error)) throw err
  // report or soft-collect the failure
  throw err
}

Prevention

When it happens

Trigger: Registering a matcher via `expect.extend({...})` and asserting against values where the matcher's own logic reports `pass: false`; the custom matcher's expectation was not met.

Common situations: The matcher logic is correct but the input does not satisfy it (a real test failure); a bug in the matcher's `pass` computation; asymmetric matcher used with unexpected input.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/23f152ae1c9e5568. Report an issue: GitHub.