sebastianbergmann/phpunit · error · EventAlreadyAssignedException

Event " " 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.

Solutions

  1. Register one subscriber interface per event and register multiple subscriber objects of that interface; the dispatcher stores a list of subscribers per event class.
  2. If the events genuinely differ, introduce a distinct event class (subclass or sibling implementing Event) for the second mapping.
  3. 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

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


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

Appendix: 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)