{"record":{"id":"143036a684e46cb1","repo":"hibernate/hibernate-orm","slug":"wrong-kind-of-binder-for-annotation-type-s-doe","errorCode":null,"errorMessage":"Wrong kind of binder for annotation type: '%s' does not accept an annotation of type '%s'","messagePattern":"Wrong kind of binder for annotation type: '(.+?)' does not accept an annotation of type '(.+?)'","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/Binders.java","lineNumber":103,"sourceCode":"\tprivate static <A extends Annotation> AttributeBinder<A> propertyBinder(Class<A> annotationType)\n\t\t\t\t\tthrows Exception {\n\t\tfinal var binderType =\n\t\t\t\tannotationType.getAnnotation( AttributeBinderType.class )\n\t\t\t\t\t\t.binder();\n\t\tcheckImplementedTypeArgument( annotationType, binderType, PropertyBinder.class );\n\t\t@SuppressWarnings(\"unchecked\") // Safe, we just checked\n\t\tfinal var castBinderType = (Class<? extends AttributeBinder<A>>) binderType;\n\t\treturn castBinderType.getDeclaredConstructor().newInstance();\n\t}\n\n\tprivate static void checkImplementedTypeArgument(\n\t\t\tClass<? extends Annotation> annotationType,\n\t\t\tClass<?> binderType, Class<?> implementedType) {\n\t\tfinal var args = typeArguments( implementedType, binderType );\n\t\tif ( args.length == 1 ) {\n\t\t\tfinal var requiredAnnotationType = args[0];\n\t\t\tif ( annotationType != requiredAnnotationType ) {\n\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\"Wrong kind of binder for annotation type:\"\n\t\t\t\t\t\t+ \" '%s' does not accept an annotation of type '%s'\"\n\t\t\t\t\t\t\t\t.formatted( binderType.getTypeName(),\n\t\t\t\t\t\t\t\t\t\tannotationType.getTypeName() )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":85,"sourceCodeEnd":113,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/Binders.java#L85-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n@AttributeBinderType(binder = MyAnnBinder.class)\npublic @interface MyAnn {}\n\npublic class MyAnnBinder implements AttributeBinder<SomeOtherAnn> { ... }\n// boot fails: MyAnnBinder does not accept an annotation of type MyAnn\n\n// after\npublic class MyAnnBinder implements AttributeBinder<MyAnn> { ... }","handlingStrategy":"validation","validationCode":"// Unit-test every binder's generic argument against its annotation before boot\nstatic void checkBinderMatchesAnnotation(Class<? extends Annotation> annotationType,\n                                         Class<?> binderClass) {\n    for ( Type t : binderClass.getGenericInterfaces() ) {\n        if ( t instanceof ParameterizedType p\n                && ( p.getRawType() == TypeBinder.class\n                     || p.getRawType() == AttributeBinder.class ) ) {\n            Class<?> arg = (Class<?>) p.getActualTypeArguments()[0];\n            if ( arg != annotationType ) {\n                throw new IllegalStateException( binderClass.getName()\n                    + \" binds \" + arg.getName() + \" but is registered on \"\n                    + annotationType.getName() );\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["hibernate","jpa","annotation-binding","custom-type","generics","type-mismatch"],"backgroundTag":"annotation-type-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}