hibernate/hibernate-orm · error · EventListenerRegistrationException

Duplicate event listener found

Error message

Duplicate event listener found

What it means

EventListenerRegistrationException thrown while appending a listener to an EventListenerGroup: a registered DuplicationStrategy matched the new listener against an already-registered one (strategy.areMatch(listener, existingListener)), and the strategy's configured action is ERROR — meaning duplicates for this event type are forbidden and registration aborts. This is the mechanism that stops the same logical listener from being wired into an event chain twice.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java:319

			//		on match - meaning no further strategies are checked...

			for ( int i = 0; i < size; i++ ) {
				final T existingListener = listenersRead[i];
				if ( traceEnabled ) {
					EVENT_LISTENER_LOGGER.tracef( "Checking incoming listener [`%s`] for match against existing listener [`%s`]",
							listener, existingListener );
				}

				if ( strategy.areMatch( listener,  existingListener ) ) {
					if ( traceEnabled ) {
						EVENT_LISTENER_LOGGER.tracef( "Found listener match between `%s` and `%s`",
								listener, existingListener );
					}

					final DuplicationStrategy.Action action = strategy.getAction();
					switch (action) {
						case ERROR:
							throw new EventListenerRegistrationException( "Duplicate event listener found" );
						case KEEP_ORIGINAL:
							if ( traceEnabled ) {
								EVENT_LISTENER_LOGGER.tracef( "Skipping listener registration (%s) : `%s`",
										action, listener );
							}
							return;
						case REPLACE_ORIGINAL:
							if ( traceEnabled ) {
								EVENT_LISTENER_LOGGER.tracef( "Replacing listener registration (%s) : `%s` -> `%s`",
										action, existingListener, listener );
							}
							prepareListener( listener );
							listenersWrite[i] = listener;
					}

					// we've found a match - we should return: the match action has already been applied at this point
					// apply all pending changes:
					setListeners( listenersWrite );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Audit registration paths and register each listener exactly once (check for duplicated Integrator jars / Spring bean wiring; mvn dependency:tree for doubled integrations)
  2. Use setListeners(type, listeners...) to replace the whole chain for that event type instead of appending, so re-runs cannot stack duplicates
  3. If the overlap is intentional, register a DuplicationStrategy with Action.KEEP_ORIGINAL or REPLACE_ORIGINAL on the group before appending

Example fix

// before
registry.getEventListenerGroup(EventType.POST_LOAD)
        .appendListeners(myListener); // called twice (two beans / two config paths) -> ERROR action fires

// after
EventListenerGroup<PostLoadEventListener> g = registry.getEventListenerGroup(EventType.POST_LOAD);
boolean present = g.listeners().stream().anyMatch(l -> l instanceof MyListener);
if (!present) g.appendListeners(new MyListener());
Defensive patterns

Strategy: validation

Validate before calling

EventListenerGroup<PersistEventListener> g = registry.getEventListenerGroup(EventType.PERSIST);
boolean already = g.listeners().stream().anyMatch(l -> l instanceof MyListener);
if (!already) {
    g.appendListeners(new MyListener());
}

Type guard

null

Try / catch

try {
    group.appendListeners(listener);
} catch (EventListenerRegistrationException e) {
    if ("Duplicate event listener found".equals(e.getMessage())) {
        log.debug("listener already registered, skipping");
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling EventListenerRegistry.getEventListenerGroup(type).appendListeners(...) twice with the same listener instance/class; two integrations (e.g. Envers plus a custom audit integrator) both registering listeners for the same EventType where a strategy like DefaultDuplicationStrategy(CLASS_EQUAL) returns ERROR; adding a listener in both hibernate.cfg.xml and an Integrator so both run at bootstrap.

Common situations: A custom Integrator discovered twice (duplicate jar on classpath, or registered both via META-INF/services and programmatically); Spring configuration building the same listener bean into two registry paths; copy-pasted bootstrap code registering the same listener in two places; version upgrades where a bundled integration now registers a listener your code also registers.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/8431c8e6750a678d. Report an issue: GitHub.