jestjs/jest · error · TypeError

${EXPECTED_COLOR('expected')} value must be a string if ${RE

Error message

${EXPECTED_COLOR('expected')} value must be a string if ${RECEIVED_COLOR('received')} value is a string

What it means

Thrown by `toContain` (matchers.ts:491) when the received value is a string but the expected value is not a string. Because string containment uses `String.indexOf`, the expected argument must also be a string; the matcher refuses to silently coerce a non-string (which would hide a real bug).

Source

Thrown at packages/expect/src/matchers.ts:492

    if (received == null) {
      throw new Error(
        matcherErrorMessage(
          matcherHint(matcherName, undefined, undefined, options),
          `${RECEIVED_COLOR('received')} value must not be null nor undefined`,
          printWithType('Received', received, printReceived),
        ),
      );
    }

    if (typeof received === 'string') {
      const wrongTypeErrorMessage = `${EXPECTED_COLOR(
        'expected',
      )} value must be a string if ${RECEIVED_COLOR(
        'received',
      )} value is a string`;

      if (typeof expected !== 'string') {
        throw new TypeError(
          matcherErrorMessage(
            matcherHint(matcherName, received, String(expected), options),
            wrongTypeErrorMessage,
            // eslint-disable-next-line prefer-template
            printWithType('Expected', expected, printExpected) +
              '\n' +
              printWithType('Received', received, printReceived),
          ),
        );
      }

      const index = received.indexOf(String(expected));
      const pass = index !== -1;

      const message = () => {
        const labelExpected = `Expected ${
          typeof expected === 'string' ? 'substring' : 'value'
        }`;

View on GitHub (pinned to 8e6d128e4a)

Solutions

  1. Pass a string substring: toContain('world'), not toContain(5) or toContain(/world/).
  2. If you need a regex match, use toMatch instead of toContain.
  3. If testing array membership (non-string received), ensure the received value is actually an array.
  4. Coerce the expected to a string with String(...) only if that is genuinely intended.

Example fix

// before
expect('hello world').toContain(/world/);
expect('items: 5').toContain(5);

// after
expect('hello world').toContain('world');
// regex belongs in toMatch
expect('hello world').toMatch(/world/);
// coerce only if intended
expect('items: 5').toContain(String(5));
Defensive patterns

Strategy: type-guard

Validate before calling

const needle = searchValue;
if (typeof received === 'string' && typeof needle !== 'string') {
  throw new Error('when received is a string, expected must also be a string');
}
expect(received).toContain(needle);

Type guard

const isStringNeedle = (received: unknown, expected: unknown): expected is string =>
  typeof received === 'string' ? typeof expected === 'string' : true;

Prevention

When it happens

Trigger: Calling `expect('hello world').toContain(5)` (number), `toContain(/world/)` (regex), or `toContain({a:1})` (object). The received is a string, so indexOf semantics require a string expected.

Common situations: Developer intends substring search but passes a number/object; confusing toContain (substring) with toMatch (regex); passing a value from a typed source that is not a string; copy-paste from an array test where toContain takes any value.

Related errors


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