hibernate/hibernate-orm · error · EventListenerRegistrationException
Listener did not implement expected interface [
Error message
Listener did not implement expected interface [
What it means
EventListenerRegistrationException from EventListenerGroupImpl.checkAgainstBaseInterface, run via prepareListener before a listener enters an event chain: the listener does not implement eventType.baseListenerInterface() for the group it is being added to. The event system dispatches by casting to the per-event interface, so a mismatched listener cannot be fired and is rejected. Generics on EventListenerGroup<T> normally catch this at compile time; the runtime check exists for raw-type and reflection-driven registrations.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java:361
// we did not find any match, add it
checkAgainstBaseInterface( listener );
additionHandler.accept( listener );
}
@SuppressWarnings("unchecked")
@AllowReflection // Possible array types are registered in org.hibernate.graalvm.internal.StaticClassLists.typesNeedingArrayCopy
@Nonnull
private T[] createListenerArrayForWrite(int len) {
return (T[]) Array.newInstance( eventType.baseListenerInterface(), len );
}
private void prepareListener(@Nonnull T listener) {
checkAgainstBaseInterface( listener );
}
private void checkAgainstBaseInterface(@Nonnull T listener) {
if ( !eventType.baseListenerInterface().isInstance( listener ) ) {
throw new EventListenerRegistrationException( "Listener did not implement expected interface ["
+ eventType.baseListenerInterface().getName() + "]" );
}
}
/**
* Implementation note: should be final for performance reasons.
* @deprecated this is not the most efficient way for iterating the event listeners.
* See {@link #fireEventOnEachListener(Object, BiConsumer)} and co. for better alternatives.
*/
@Override
@Deprecated
public final @Nonnull Iterable<T> listeners() {
return listenersAsList;
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Register each listener only on the EventListenerGroup whose EventType's baseListenerInterface it implements (PersistEventListener -> EventType.PERSIST, PostLoadEventListener -> EventType.POST_LOAD, etc.)
- Keep EventListenerGroup<T> generic in your code — avoid raw types so mismatches fail at compile time
- Make proxy/decorator wrappers implement the same listener interface as the wrapped instance (or skip wrapping)
Example fix
// before EventListenerGroup raw = registry.getEventListenerGroup(EventType.POST_INSERT); raw.appendListeners(new MyPostLoadListener()); // raw type hides mismatch -> Listener did not implement expected interface // after EventListenerGroup<PostInsertEventListener> g = registry.getEventListenerGroup(EventType.POST_INSERT); g.appendListeners(new MyPostInsertListener()); // compiles only when types line up
Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
boolean implementsBase(EventType<?> type, Object listener) {
return type.baseListenerInterface().isInstance(listener);
}
// EventType#baseListenerInterface is public; call before appendListeners Try / catch
try {
group.appendListeners(listener);
} catch (EventListenerRegistrationException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Listener did not implement")) {
// wrong group or wrong listener class — fix the EventType/listener pairing
}
throw e;
} Prevention
- Always type EventListenerGroup<T> with the listener interface — never raw types
- Pair listener registrations by interface family (one constant per listener class) in a helper method
- Make wrapper/proxy listeners implement the same interface as the wrapped target
When it happens
Trigger: Appending a listener to the wrong group — e.g. adding a PostInsertEventListener to the EventType.POST_LOAD group; using raw EventListenerGroup types (unchecked appendListeners) so the compiler cannot police the type; listener wrappers/proxies (metrics, tracing) that fail to implement the original listener interface; integrations that look up groups by EventType but construct listeners of a different family.
Common situations: Copy-pasted integration code where the EventType constant and listener class no longer match; upgrading Hibernate across majors where listener interfaces moved between packages and the adapter now implements the wrong one; hand-rolled proxies around listeners that only extend Object.
Related errors
- Duplicate event listener found
- jakarta.persistence.validation.group.{} is of unknown type:
- Given object was not an instance of {} [{}]
- Could not instantiate event listener '{}'
- Configuration property hibernate.jdbc.time_zone value [{}] i
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/c9e99788106e758d.
Report an issue: GitHub.