vitest-dev/vitest · error · TypeError

Asymmetric matcher does not implement toAsymmetricMatcher()

Error message

Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher()

What it means

The AsymmetricMatcher pretty-format plugin serializes objects carrying the asymmetric matcher $$typeof symbol. After handling built-in matchers (StringMatching, StringContaining and their negations), if the matcher object has no toAsymmetricMatcher() method the plugin cannot render it and throws. This protects against custom matcher objects that declare the asymmetric typeof but skip the serialization contract.

Example fix

// before
class FooMatcher { $$typeof = asymmetricMatcher; sample = 'x' }
// after
class FooMatcher { $$typeof = asymmetricMatcher; sample = 'x'; toAsymmetricMatcher() { return `Foo<${this.sample}>` } }
Defensive patterns

Strategy: type-guard

Type guard

function isSerializableMatcher(v: any): boolean {
  return v && v.$$typeof === Symbol.for('jest.asymmetricMatcher') && typeof v.toAsymmetricMatcher === 'function'
}

Prevention

When it happens

Trigger: Defining a custom asymmetric matcher (object with $$typeof equal to the asymmetricMatcher symbol) without implementing toAsymmetricMatcher(); passing such a matcher to expect/pretty-format output.

Common situations: Extending expect with a custom matcher class that sets $$typeof but forgets the serialize method; using a matcher from a library that does not implement the full asymmetric matcher interface; version mismatch in the asymmetric matcher symbol.

Related errors


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

Appendix: source

Thrown at packages/pretty-format/src/plugins/AsymmetricMatcher.ts:84

      stringedValue
      + SPACE
      + printer(val.sample, config, indentation, depth, refs)
    )
  }

  if (
    stringedValue === 'StringContaining'
    || stringedValue === 'StringNotContaining'
  ) {
    return (
      stringedValue
      + SPACE
      + printer(val.sample, config, indentation, depth, refs)
    )
  }

  if (typeof val.toAsymmetricMatcher !== 'function') {
    throw new TypeError(
      `Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher()`,
    )
  }

  return val.toAsymmetricMatcher()
}

const test: NewPlugin['test'] = (val: any) =>
  val && val.$$typeof === asymmetricMatcher

const plugin: NewPlugin = { serialize, test }

export default plugin

View on GitHub (pinned to 1fa9837ec2)