sebastianbergmann/phpunit · error · UnknownSubscriberException
Subscriber "%s" does not exist or is not an interface
Error message
Subscriber "%s" does not exist or is not an interface
What it means
The first argument of TypeMap::addMapping() must name an interface; ensureSubscriberInterfaceExists() runs interface_exists() (which triggers autoload) and throws UnknownSubscriberException when the string is neither a loaded nor autoloadable interface. The distinction matters: the error fires at registration time for a bad FQCN, before any dispatching happens.
Source
Thrown at src/Event/TypeMap.php:97
}
throw new MapError(
sprintf(
'Subscriber "%s" does not implement a known interface',
$subscriber::class,
),
);
}
/**
* @param class-string $subscriberInterface
*
* @throws UnknownSubscriberException
*/
private function ensureSubscriberInterfaceExists(string $subscriberInterface): void
{
if (!interface_exists($subscriberInterface)) {
throw new UnknownSubscriberException(
sprintf(
'Subscriber "%s" does not exist or is not an interface',
$subscriberInterface,
),
);
}
}
/**
* @param class-string $eventClass
*
* @throws UnknownEventException
*/
private function ensureEventClassExists(string $eventClass): void
{
if (!class_exists($eventClass)) {
throw new UnknownEventException(
sprintf(View on GitHub (pinned to f123cdb2a2)
Solutions
- Pass the interface name via the ::class constant (MySubscriberInterface::class) instead of a string literal so typos surface as PHP errors.
- Run 'composer dump-autoload' and confirm interface_exists('App\Test\MySubscriberInterface') returns true in 'php -r'.
- If the name refers to a class, convert it to an interface or pass the actual interface the subscribers will implement.
Example fix
// before
$typeMap->addMapping('App\Test\MySubscriberrInterface', MyEvent::class); // typo + literal
// after
use App\Test\MySubscriberInterface;
$typeMap->addMapping(MySubscriberInterface::class, MyEvent::class); Defensive patterns
Strategy: validation
Validate before calling
if (!interface_exists($subscriberInterface)) {
throw new InvalidArgumentException($subscriberInterface . ' is not a loadable interface; fix the FQCN or run composer dump-autoload');
}
$typeMap->addMapping($subscriberInterface, $eventClass); Type guard
/** @phpstan-assert class-string $name */
function assertInterfaceName(string $name): void
{
if (!interface_exists($name)) {
throw new InvalidArgumentException("Interface '{$name}' does not exist");
}
} Prevention
- Pass interface names via ::class constants, never string literals.
- Run composer dump-autoload after adding namespaces or changing PSR-4 mappings.
- Re-check custom FQCNs after PHPUnit upgrades.
When it happens
Trigger: addMapping('App\Test\MySubscriberInterface', ...) where that name is misspelled, is actually a class rather than an interface, or lives in a file Composer has not mapped; passing a string with stray whitespace or a doubled backslash; referencing an interface renamed between versions.
Common situations: Wiring custom event types in extension bootstrap code; forgetting 'composer dump-autoload' after adding a new namespace or changing autoload-dev PSR-4 prefixes; upgrading PHPUnit where subscriber interface namespaces changed while custom mappings kept old FQCNs.
Related errors
- Event class "%s" does not exist
- Subscriber "%s" does not implement a known interface
- Subscriber "%s" does not extend Subscriber interface
- Event "%s" does not implement Event interface
- Subscriber type "%s" already registered
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/755fa53466b430cb.
Report an issue: GitHub.