sebastianbergmann/comparator · error · RuntimeException

No suitable Comparator implementation found

Error message

No suitable Comparator implementation found

What it means

ComparatorFactory::getComparatorFor() iterates its registered comparators and throws this RuntimeException when no comparator's accepts($expected, $actual) returns true. It is a fallback that should be unreachable because the factory registers comparators covering all types, but it fires if the chain cannot handle the value pair.

Solutions

  1. Call (new Factory)->reset() or ensure the default comparator chain is registered before comparing
  2. Register a custom comparator whose accepts() covers the value pair being compared
  3. Check that test teardown (reset/unregister) is not leaving the factory empty for subsequent comparisons
  4. Use the shared default Factory::getInstance()-style setup instead of a hand-built factory with missing comparators

Example fix

// before
$factory = new Factory;
$factory->unregister(new ObjectComparator); // chain now can't handle objects
$factory->getComparatorFor($a, $b); // RuntimeException
// after
$factory = new Factory;
$factory->register(new MyCustomComparator);
$factory->getComparatorFor($a, $b)->assertEquals($a, $b);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!$factory->getComparatorFor(...)) — instead verify the factory retains defaults: avoid unregister/reset in production paths

Try / catch

try {
    $comparator = $factory->getComparatorFor($expected, $actual);
} catch (\RuntimeException $e) {
    // rebuild factory with defaults or fall back to === comparison
    $comparator = (new Factory)->reset()->getComparatorFor($expected, $actual);
}

Prevention

When it happens

Trigger: Calling Factory->getComparatorFor($expected, $actual) with a value pair no registered comparator accepts — typically only possible when all comparators have been removed via unregister()/reset() or when a custom factory instance is built without the default comparators.

Common situations: Tests that call unregister() or reset() on the factory and then try to compare values; building a Factory with only custom comparators that don't accept the given types; a custom comparator registered with an overly narrow accepts() shadowing defaults after reset().

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of sebastianbergmann/comparator@00837a9d22 (2026-09-15). Data as JSON: /api/errors/1fb607cd70645d49. Report an issue: GitHub.

Appendix: source

Thrown at src/Factory.php:115

        $this->closureComparisonOccurred = false;
    }

    public function getComparatorFor(mixed $expected, mixed $actual): Comparator
    {
        foreach ($this->customComparators as $comparator) {
            if ($comparator->accepts($expected, $actual)) {
                return $comparator;
            }
        }

        foreach ($this->defaultComparators as $comparator) {
            if ($comparator->accepts($expected, $actual)) {
                return $comparator;
            }
        }

        // @codeCoverageIgnoreStart
        throw new RuntimeException('No suitable Comparator implementation found');
        // @codeCoverageIgnoreEnd
    }

    /**
     * Registers a new comparator.
     *
     * This comparator will be returned by getComparatorFor() if its accept() method
     * returns TRUE for the compared values. It has higher priority than the
     * existing comparators, meaning that its accept() method will be invoked
     * before those of the other comparators.
     */
    public function register(Comparator $comparator): void
    {
        array_unshift($this->customComparators, $comparator);

        $comparator->setFactory($this);
    }

View on GitHub (pinned to 00837a9d22)