hibernate/hibernate-orm · error · AnnotationException
Wrong kind of binder for annotation type: '%s' does not acce
Error message
Wrong kind of binder for annotation type: '%s' does not accept an annotation of type '%s'
What it means
A custom binder must implement TypeBinder<A> or AttributeBinder<A> parameterized with exactly the annotation type it is registered on via @TypeBinderType/@AttributeBinderType. Binders.checkImplementedTypeArgument resolves the binder's implemented type argument at boot and throws this AnnotationException when it differs from the annotated annotation type. This fails fast instead of letting the binder receive a wrong-typed annotation and crash later.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/Binders.java:103
private static <A extends Annotation> AttributeBinder<A> propertyBinder(Class<A> annotationType)
throws Exception {
final var binderType =
annotationType.getAnnotation( AttributeBinderType.class )
.binder();
checkImplementedTypeArgument( annotationType, binderType, PropertyBinder.class );
@SuppressWarnings("unchecked") // Safe, we just checked
final var castBinderType = (Class<? extends AttributeBinder<A>>) binderType;
return castBinderType.getDeclaredConstructor().newInstance();
}
private static void checkImplementedTypeArgument(
Class<? extends Annotation> annotationType,
Class<?> binderType, Class<?> implementedType) {
final var args = typeArguments( implementedType, binderType );
if ( args.length == 1 ) {
final var requiredAnnotationType = args[0];
if ( annotationType != requiredAnnotationType ) {
throw new AnnotationException(
"Wrong kind of binder for annotation type:"
+ " '%s' does not accept an annotation of type '%s'"
.formatted( binderType.getTypeName(),
annotationType.getTypeName() )
);
}
}
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Change the binder's generic parameter to the annotation it processes: class MyBinder implements AttributeBinder<MyAnn>
- If one logic must serve several annotations, write one thin binder class per annotation and delegate to shared code
- Add a unit test that reflects the binder's ParameterizedType argument and asserts it equals the annotation class, so this fails at test time not boot time
Example fix
// before
@AttributeBinderType(binder = MyAnnBinder.class)
public @interface MyAnn {}
public class MyAnnBinder implements AttributeBinder<SomeOtherAnn> { ... }
// boot fails: MyAnnBinder does not accept an annotation of type MyAnn
// after
public class MyAnnBinder implements AttributeBinder<MyAnn> { ... } Defensive patterns
Strategy: validation
Validate before calling
// Unit-test every binder's generic argument against its annotation before boot
static void checkBinderMatchesAnnotation(Class<? extends Annotation> annotationType,
Class<?> binderClass) {
for ( Type t : binderClass.getGenericInterfaces() ) {
if ( t instanceof ParameterizedType p
&& ( p.getRawType() == TypeBinder.class
|| p.getRawType() == AttributeBinder.class ) ) {
Class<?> arg = (Class<?>) p.getActualTypeArguments()[0];
if ( arg != annotationType ) {
throw new IllegalStateException( binderClass.getName()
+ " binds " + arg.getName() + " but is registered on "
+ annotationType.getName() );
}
}
}
} Prevention
- When creating a new custom annotation, copy its binder skeleton from the same annotation, not from an unrelated one
- Keep one binder class per annotation type; share logic through composition, not through generic reuse
- Add the generic-argument reflection check to the test suite so mismatches fail at test time
When it happens
Trigger: class MyBinder implements AttributeBinder<SomeOtherAnn> is referenced from @AttributeBinderType(binder = MyBinder.class) on annotation type MyAnn. During binder lookup, typeArguments(TypeBinder/AttributeBinder, binderType) returns one argument, args[0] != annotationType, and the exception fires with both type names.
Common situations: Copy-pasting a binder from another custom annotation without updating the generic parameter; renaming the annotation class but not the binder's type argument; registering a generic/reusable binder on multiple annotation types without subclasses per annotation.
Related errors
- Error processing @TypeBinderType annotation '%s' for embedda
- Error processing @TypeBinderType annotation '%s' for entity
- error processing @AttributeBinderType annotation '%s' for at
- @Convert placed on @Entity/@MappedSuperclass must define att
- Collection '{}' annotated '@NotFound' is not a '@ManyToMany'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/143036a684e46cb1.
Report an issue: GitHub.