{"id":"a049b3b2daa77768","repo":"spring-projects/spring-framework","slug":"value-annotation-must-have-a-value-attribute","errorCode":null,"errorMessage":"Value annotation must have a value attribute","messagePattern":"Value annotation must have a value attribute","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java","lineNumber":443,"sourceCode":"\tprotected @Nullable Object findValue(Annotation[] annotationsToSearch) {\n\t\tif (annotationsToSearch.length > 0) {   // qualifier annotations have to be local\n\t\t\tAnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(\n\t\t\t\t\tAnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);\n\t\t\tif (attr != null) {\n\t\t\t\treturn extractValue(attr);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Extract the value attribute from the given annotation.\n\t * @since 4.3\n\t */\n\tprotected Object extractValue(AnnotationAttributes attr) {\n\t\tObject value = attr.get(AnnotationUtils.VALUE);\n\t\tif (value == null) {\n\t\t\tthrow new IllegalStateException(\"Value annotation must have a value attribute\");\n\t\t}\n\t\treturn value;\n\t}\n\n}\n","sourceCodeStart":425,"sourceCodeEnd":449,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java#L425-L449","documentation":"QualifierAnnotationAutowireCandidateResolver.extractValue reads the 'value' attribute from the configured value annotation type (default @Value). If a custom value annotation type was set via setValueAnnotationType and that annotation has no attribute literally named 'value', or the attribute is absent on the occurrence, this is thrown. With the default @Value it essentially never fires because @Value always defines value().","triggerScenarios":"A custom annotation is registered as the valueAnnotationType (replacing @Value) but that annotation type does not declare a member named 'value', so attr.get(VALUE) returns null.","commonSituations":"Replacing @Value with a bespoke annotation that omits a 'value()' member; migrating from a custom annotation whose attribute was renamed; library/fixture code that registers a non-conforming annotation type.","solutions":["Ensure the custom value annotation declares a String value() default ...; attribute (it is read by the literal name 'value').","If renaming an attribute, also keep/alias a 'value' member or stop registering it as the valueAnnotationType.","Revert to the default @Value annotation type if a custom one is not strictly required."],"exampleFix":"// before\n@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)\npublic @interface MyValue { String configKey(); } // no 'value' member\n\nresolver.setValueAnnotationType(MyValue.class);\n\n// after\npublic @interface MyValue { String value(); }","handlingStrategy":"validation","validationCode":"Class<? extends Annotation> t = resolver.getValueAnnotationType();\nMethod value = t.getDeclaredMethod(\"value\");\nif (value.getReturnType() != String.class) throw new IllegalStateException(t + \" needs String value()\");","typeGuard":"boolean hasValueMember(Class<? extends Annotation> annoType) {\n  try { return annoType.getDeclaredMethod(\"value\").getReturnType() == String.class; }\n  catch (NoSuchMethodException e) { return false; }\n}","tryCatchPattern":null,"preventionTips":["Keep the default @Value unless a custom annotation is essential.","Any custom value annotation must declare a String value() member.","Add a startup assertion that validates the configured valueAnnotationType has a 'value' member."],"tags":["spring","injection","value-annotation","configuration"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}