sebastianbergmann/phpunit · error · EventAlreadyAssignedException
Event "%s" already assigned
Error message
Event "%s" already assigned
What it means
TypeMap enforces a strict one-to-one relation: each event class may be assigned to exactly one subscriber interface. ensureEventWasNotAlreadyAssigned() scans the mapping values with in_array($eventClass, $this->mapping, true) and throws EventAlreadyAssignedException when the event is already claimed by another (or the same) interface.
Source
Thrown at src/Event/TypeMap.php:182
if (array_key_exists($subscriberInterface, $this->mapping)) {
throw new SubscriberTypeAlreadyRegisteredException(
sprintf(
'Subscriber type "%s" already registered',
$subscriberInterface,
),
);
}
}
/**
* @param class-string $eventClass
*
* @throws EventAlreadyAssignedException
*/
private function ensureEventWasNotAlreadyAssigned(string $eventClass): void
{
if (in_array($eventClass, $this->mapping, true)) {
throw new EventAlreadyAssignedException(
sprintf(
'Event "%s" already assigned',
$eventClass,
),
);
}
}
}
View on GitHub (pinned to f123cdb2a2)
Solutions
- Register one subscriber interface per event and register multiple subscriber objects of that interface; the dispatcher stores a list of subscribers per event class.
- If the events genuinely differ, introduce a distinct event class (subclass or sibling implementing Event) for the second mapping.
- Drop or rename the conflicting mapping so each event class appears once.
Example fix
// before $typeMap->addMapping(ExecutionFinishedSubscriber::class, ExecutionFinished::class); $typeMap->addMapping(MetricsFlushSubscriber::class, ExecutionFinished::class); // already assigned // after $typeMap->addMapping(ExecutionFinishedSubscriber::class, ExecutionFinished::class); $typeMap->addMapping(MetricsFlushSubscriber::class, MetricsFlushed::class); // distinct event // or register two ExecutionFinishedSubscriber instances instead
Defensive patterns
Strategy: try-catch
Try / catch
try {
$typeMap->addMapping($subscriberInterface, $eventClass);
} catch (PHPUnit\Event\EventAlreadyAssignedException $e) {
// One event maps to one interface: register subscriber *instances* instead of a second interface
} Prevention
- Remember the mapping is one-to-one; add multiple subscriber objects of the single mapped interface for the same event.
- Audit custom mappings for event classes that duplicate built-in events.
- Introduce a distinct event subclass when a second event type is genuinely needed.
When it happens
Trigger: addMapping(SecondSubscriberInterface::class, SameEvent::class) after (FirstSubscriberInterface, SameEvent) is registered; re-adding a built-in event class, since Facade::registerDefaultTypes() already claims every stock event class.
Common situations: Wanting two different subscriber types for the same event (the design answer is multiple subscriber instances of the one interface, not two interfaces); merging a custom map over the default map; refactoring one subscriber interface into several while keeping the event.
Related errors
- Subscriber "%s" does not implement a known interface
- Subscriber type "%s" already registered
- Subscriber "%s" does not exist or is not an interface
- Event class "%s" does not exist
- Subscriber "%s" does not extend Subscriber interface
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/967761ff9c2857ff.
Report an issue: GitHub.