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

  1. Pass the interface name via the ::class constant (MySubscriberInterface::class) instead of a string literal so typos surface as PHP errors.
  2. Run 'composer dump-autoload' and confirm interface_exists('App\Test\MySubscriberInterface') returns true in 'php -r'.
  3. 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

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


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/755fa53466b430cb. Report an issue: GitHub.