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
- Call (new Factory)->reset() or ensure the default comparator chain is registered before comparing
- Register a custom comparator whose accepts() covers the value pair being compared
- Check that test teardown (reset/unregister) is not leaving the factory empty for subsequent comparisons
- 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
- Never unregister all comparators or call reset() in shared/global factory state
- Always register a catch-all custom comparator if you build a factory with only custom comparators
- Scope reset()/unregister() to test setup/teardown with try/finally
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
- Failed asserting that two values of enumeration
- Failed asserting that two Number objects are equal.
- Failed asserting that
- is not instance of expected class " ".
- Failed asserting that two arrays are equal.
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)