{"record":{"id":"2deb0be9252a5c56","repo":"spring-projects/spring-framework","slug":"qualifier-type-needs-to-be-annotation-type","errorCode":null,"errorMessage":"Qualifier type [{}] needs to be annotation type","messagePattern":"Qualifier type \\[(.+?)\\] needs to be annotation type","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.java","lineNumber":115,"sourceCode":"\t\t\tif (!(dlbf.getAutowireCandidateResolver() instanceof QualifierAnnotationAutowireCandidateResolver)) {\n\t\t\t\tdlbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());\n\t\t\t}\n\t\t\tQualifierAnnotationAutowireCandidateResolver resolver =\n\t\t\t\t\t(QualifierAnnotationAutowireCandidateResolver) dlbf.getAutowireCandidateResolver();\n\t\t\tfor (Object value : this.customQualifierTypes) {\n\t\t\t\tClass<? extends Annotation> customType = null;\n\t\t\t\tif (value instanceof Class) {\n\t\t\t\t\tcustomType = (Class<? extends Annotation>) value;\n\t\t\t\t}\n\t\t\t\telse if (value instanceof String className) {\n\t\t\t\t\tcustomType = (Class<? extends Annotation>) ClassUtils.resolveClassName(className, this.beanClassLoader);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Invalid value [\" + value + \"] for custom qualifier type: needs to be Class or String.\");\n\t\t\t\t}\n\t\t\t\tif (!Annotation.class.isAssignableFrom(customType)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Qualifier type [\" + customType.getName() + \"] needs to be annotation type\");\n\t\t\t\t}\n\t\t\t\tresolver.addQualifierType(customType);\n\t\t\t}\n\t\t}\n\t}\n\n}\n","sourceCodeStart":97,"sourceCodeEnd":124,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-beans/src/main/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.java#L97-L124","documentation":"Thrown by CustomAutowireConfigurer when a resolved custom qualifier type is not an annotation type (i.e., does not extend java.lang.annotation.Annotation). After resolving the Class or String to a Class object, the check Annotation.class.isAssignableFrom(customType) at line 114 fails for non-annotation classes.","triggerScenarios":"Specifying a regular class or interface (not annotated with @Target/@Retention/@interface) as a custom qualifier type. For example, passing com.example.SomeService.class (a regular class) instead of a custom @Qualifier-style annotation.","commonSituations":"Confusing a service/component class with a qualifier annotation. Typing the wrong fully-qualified class name in XML, resolving to a non-annotation class. Passing an interface that is not an annotation interface.","solutions":["Ensure the specified type is a Java annotation (declared with @interface).","Create a proper custom qualifier annotation annotated with @Target and @Retention.","Verify the class name resolves to an annotation type, not a regular class or interface."],"exampleFix":"// before\nconfigurer.setCustomQualifierTypes(\n    Set.of(MyService.class)); // regular class, not annotation\n\n// after\n@Target({ElementType.FIELD, ElementType.PARAMETER})\n@Retention(RetentionPolicy.RUNTIME)\n@Qualifier\npublic @interface MyQualifier { }\n\nconfigurer.setCustomQualifierTypes(\n    Set.of(MyQualifier.class)); // annotation type","handlingStrategy":"validation","validationCode":"// Validate that each qualifier type is an annotation before registering\nfor (Object value : customQualifierTypes) {\n    Class<?> type = (value instanceof Class<?> c) ? c\n        : ClassUtils.resolveClassName((String) value, classLoader);\n    if (!Annotation.class.isAssignableFrom(type)) {\n        throw new IllegalArgumentException(\n            type.getName() + \" is not an annotation type\");\n    }\n}","typeGuard":"// Type guard: ensure a class is an annotation before using as qualifier\nstatic boolean isQualifierAnnotation(Class<?> type) {\n    return type != null && Annotation.class.isAssignableFrom(type);\n}","tryCatchPattern":null,"preventionTips":["Custom qualifier types must be Java annotations (declared with @interface).","Verify each type with Annotation.class.isAssignableFrom() before adding it.","Create proper qualifier annotations annotated with @Target and @Retention(RUNTIME)."],"tags":["spring","custom-autowire","annotation","configuration"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}