hibernate/hibernate-orm · error · HibernateException

Custom event-type already registered:

Error message

Custom event-type already registered: 

What it means

HibernateException from EventEngine's registerEventType: an event type with the given name is already present in the engine's map — the map holds both the standard types (registered via EventType.registerStandardTypes: load, flush, save, merge, ...) and previously contributed custom types, and names must be unique across both. The message includes the existing EventType that owns the name.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java:118

		@Nonnull
		public <T> EventType<T> contributeEventType(@Nonnull String name, @Nonnull Class<T> listenerRole) {
			final var eventType = registerEventType( name, listenerRole );
			listenerRegistryBuilder.prepareListeners( eventType );
			return eventType;
		}

		@Nonnull
		private <T> EventType<T> registerEventType(@Nonnull String name, @Nonnull Class<T> listenerRole) {
			if ( name == null ) {
				throw new HibernateException( "Custom event-type name must be non-null." );
			}
			else if ( listenerRole == null ) {
				throw new HibernateException( "Custom event-type listener role must be non-null." );
			}
			// make sure it does not match an existing name...
			else if ( eventTypes.containsKey( name ) ) {
				final var existing = eventTypes.get( name );
				throw new HibernateException(
						"Custom event-type already registered: " + name + " => " + existing
				);
			}
			else {
				final EventType<T> eventType = EventType.create( name, listenerRole, eventTypes.size() );
				eventTypes.put( name, eventType );
				return eventType;
			}
		}

		@Override
		@Nonnull
		@SafeVarargs
		public final <T> EventType<T> contributeEventType(@Nonnull String name, @Nonnull Class<T> listenerRole, @Nonnull T... defaultListeners) {
			final var eventType = contributeEventType( name, listenerRole );
			if ( defaultListeners != null ) {
				listenerRegistryBuilder.getListenerGroup( eventType ).appendListeners( defaultListeners );
			}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Namespace custom event-type names (e.g. 'com.acme.hr.audit') so they cannot collide with standard or other custom names
  2. Before contributing, check findEventType(name) on the EventEngineContributions and skip or reuse when the name is already registered
  3. Eliminate duplicate contributor discovery: inspect the fat jar's merged META-INF/services and run mvn dependency:tree to find doubled integration artifacts

Example fix

// before
public void contribute(EventEngineContributions c) {
    c.contributeEventType("audit", AuditListener.class); // collides with standard/other 'audit'
}

// after
public void contribute(EventEngineContributions c) {
    if (c.findEventType("com.acme.audit") == null) {
        c.contributeEventType("com.acme.audit", AuditListener.class);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

public void contribute(EventEngineContributions c) {
    if (c.findEventType("com.acme.audit") == null) {
        c.contributeEventType("com.acme.audit", MyAuditListener.class);
    }
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: An EventEngineContributor contributing a custom event type whose name collides with a standard one ('load', 'flush', 'persist', ...) or with another contributor's custom type; the same contributor being discovered twice (duplicate jar on the classpath / duplicated META-INF/services entries) so its contribution runs twice per SessionFactory build.

Common situations: Two versions of an integration library both shipping the same EventEngineContributor service; fat-jar/shading merging service files so the contributor lists twice; generic names like 'audit' or 'notify' colliding between first-party and third-party integrations.

Related errors


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