hibernate/hibernate-orm · error · ModelsException

Callback method annotated '@

Error message

Callback method annotated '@%s' in '%s' must return void and accept one argument: %s

What it means

Every entity-listener callback method must follow the JPA signature: return void and accept exactly one argument (the entity). GlobalRegistrationsImpl.applyTargetedCallback checks each method carrying a callback annotation via LifecycleEventHandler.matchesSignature and throws naming the annotation, the listener class, and the offending method details.

Solutions

  1. Change the method to return void and accept exactly one parameter typed as the entity (or Object): @PreUpdate void onUpdate(Item item)
  2. Obtain any extra context inside the method body (injected beans, static helpers) rather than as parameters
  3. Leave non-conforming helper methods unannotated - only real callbacks should carry the annotation

Example fix

// before
@PreUpdate
boolean beforeUpdate(Item item, EntityManager em) { return item.isValid(); }

// after
@PreUpdate
void beforeUpdate(Item item) {
    if ( !item.isValid() ) throw new IllegalStateException("invalid item");
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: validate every @Pre*/@Post* method in listener classes
for (Class<?> listener : listeners) {
    for (java.lang.reflect.Method m : listener.getDeclaredMethods()) {
        boolean isCallback = java.util.Arrays.stream(m.getAnnotations())
                .anyMatch(a -> a.annotationType().getName().startsWith("jakarta.persistence."));
        if (isCallback && !isValidCallback(m))
            throw new IllegalStateException("Bad callback signature: " + m);
    }
}

Type guard

// Narrows a reflection Method to a valid JPA entity-listener callback
static boolean isValidCallback(java.lang.reflect.Method m) {
    return m.getReturnType() == void.class && m.getParameterCount() == 1;
}

Try / catch

catch (org.hibernate.models.ModelsException e) during bootstrap: the message names the annotation, class and method - change that method to return void with exactly one entity parameter

Prevention

When it happens

Trigger: A listener method annotated with a callback annotation that returns a value (boolean/String), takes zero arguments, or takes two or more - e.g. boolean beforeUpdate(Item item, EntityManager em) annotated @PreUpdate.

Common situations: Porting boolean 'validate' style hooks from other frameworks; adding context parameters (EntityManager, audit context) to callbacks; converting interceptors to JPA listeners without adjusting signatures.

Related errors


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

Appendix: source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java:815

	}

	private void addTargetedJpaEventListener(ClassDetails targetClass, LifecycleEventHandler listener) {
		if ( targetedLifecycleEventHandlers == null ) {
			targetedLifecycleEventHandlers = new LinkedHashMap<>();
		}

		targetedLifecycleEventHandlers.computeIfAbsent( targetClass, ignored -> new ArrayList<>() ).add( listener );
	}

	private static void applyTargetedCallback(
			ClassDetails listenerClassDetails,
			MethodDetails methodDetails,
			Class<? extends Annotation> callbackAnnotation,
			CallbackType callbackType,
			Map<ClassDetails, TargetedLifecycleEventHandlerBuilder> builders) {
		if ( methodDetails.hasDirectAnnotationUsage( callbackAnnotation ) ) {
			if ( !LifecycleEventHandler.matchesSignature( JpaEventListenerStyle.LISTENER, methodDetails ) ) {
				throw new ModelsException( "Callback method annotated '@"
						+ callbackAnnotation.getSimpleName() + "' in '"
						+ listenerClassDetails.getClassName()
						+ "' must return void and accept one argument: " + methodDetails );
			}

			builders.computeIfAbsent( methodDetails.getArgumentTypes().get( 0 ),
							ignored -> new TargetedLifecycleEventHandlerBuilder() )
					.setCallbackMethod( callbackType, methodDetails );
		}
	}

	private static class TargetedLifecycleEventHandlerBuilder {
		private final EnumMap<CallbackType, MethodDetails> callbackMethods = new EnumMap<>( CallbackType.class );

		private TargetedLifecycleEventHandlerBuilder() {
		}

		private void setCallbackMethod(CallbackType callbackType, MethodDetails method) {

View on GitHub (pinned to fad1729dce)