quarkusio/quarkus · error · IllegalArgumentException

Methods that are annotated with JPA Listener annotations sho

Error message

Methods that are annotated with JPA Listener annotations should not be private. Offending method is '${declaringClass}#${method}'

What it means

Hibernate ORM's CDI processor transforms JPA listener callback methods (e.g. @PrePersist, @PostLoad annotated methods on entity listeners) to work with Quarkus bean injection. A private callback method cannot be safely transformed because the client proxy would copy the method without the injected fields, so the build fails fast with an IllegalArgumentException naming the offending class#method.

Source

Thrown at extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java:325

                        MethodInfo method = target.asMethod();
                        if (Modifier.isPrivate(method.flags())) {
                            if (beanScope.getDotName().equals(BuiltinScope.SINGLETON.getName())) {
                                // we can safely transform in this case
                                producer.produce(new BytecodeTransformerBuildItem(method.declaringClass().name().toString(),
                                        new BiFunction<>() {
                                            @Override
                                            public ClassVisitor apply(String cls, ClassVisitor clsVisitor) {
                                                var classTransformer = new ClassTransformer(cls);
                                                classTransformer.modifyMethod(MethodDescriptor.of(method))
                                                        .removeModifiers(Modifier.PRIVATE);
                                                return classTransformer.applyTo(clsVisitor);
                                            }
                                        }));
                            } else {
                                // we can't transform because the client proxy does not know about the transformation and
                                // will therefore simply copy the private method which will then likely fail because it does
                                // not contain the injected fields
                                throw new IllegalArgumentException(
                                        "Methods that are annotated with JPA Listener annotations should not be private. Offending method is '"
                                                + method.declaringClass().name() + "#" + method.name() + "'");
                            }
                        }
                    }
                }
            } else {
                // we don't really know what to do here, just bail and CDI will figure it out
            }
        }
    }

    @BuildStep
    void registerAnnotations(BuildProducer<AdditionalBeanBuildItem> additionalBeans,
            BuildProducer<BeanDefiningAnnotationBuildItem> beanDefiningAnnotations) {
        // add the @PersistenceUnit, @PersistenceUnitExtension, @JsonFormat and @XmlFormat classes
        // otherwise they won't be registered as qualifiers
        additionalBeans.produce(AdditionalBeanBuildItem.builder()

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the offending method's visibility from private to at least package-private (recommended by the JPA spec) or public
  2. Remove bean injection from the listener so no transformation is needed, keeping in mind private listeners are still non-standard
  3. Refactor the callback logic into a CDI event observer instead of a JPA listener

Example fix

// before
@Entity
public class User {
    @PrePersist
    private void onCreate() { ... }
}
// after
@Entity
public class User {
    @PrePersist
    void onCreate() { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// In your entity/listener classes, audit callbacks before build:
for (java.lang.reflect.Method m : MyListener.class.getDeclaredMethods()) {
    if (java.lang.reflect.Modifier.isPrivate(m.getModifiers()) &&
        java.util.Arrays.stream(m.getAnnotations()).anyMatch(a ->
            a.annotationType().getName().startsWith("jakarta.persistence."))) {
        throw new IllegalStateException("JPA callback must not be private: " + m);
    }
}

Prevention

When it happens

Trigger: A build-step in transformBeans encounters an entity listener or entity method annotated with a JPA listener annotation (@PrePersist, @PostUpdate, @PreRemove, @PostPersist, @PostUpdate, @PostLoad) that is declared private and whose declaring class cannot be transformed (the client proxy would not know about the transformation).

Common situations: Defining lifecycle callback methods as private inside @Entity or @EntityListener classes; migrating a legacy Hibernate app where callbacks were made private for encapsulation.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/e27b81df736d8bb5. Report an issue: GitHub.