spring-projects/spring-framework · error · IllegalStateException
Value annotation must have a value attribute
Error message
Value annotation must have a value attribute
What it means
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().
Source
Thrown at spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java:443
protected @Nullable Object findValue(Annotation[] annotationsToSearch) {
if (annotationsToSearch.length > 0) { // qualifier annotations have to be local
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
if (attr != null) {
return extractValue(attr);
}
}
return null;
}
/**
* Extract the value attribute from the given annotation.
* @since 4.3
*/
protected Object extractValue(AnnotationAttributes attr) {
Object value = attr.get(AnnotationUtils.VALUE);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
}
}
View on GitHub (pinned to e8729d0438)
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.
Example fix
// before
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)
public @interface MyValue { String configKey(); } // no 'value' member
resolver.setValueAnnotationType(MyValue.class);
// after
public @interface MyValue { String value(); } Defensive patterns
Strategy: validation
Validate before calling
Class<? extends Annotation> t = resolver.getValueAnnotationType();
Method value = t.getDeclaredMethod("value");
if (value.getReturnType() != String.class) throw new IllegalStateException(t + " needs String value()"); Type guard
boolean hasValueMember(Class<? extends Annotation> annoType) {
try { return annoType.getDeclaredMethod("value").getReturnType() == String.class; }
catch (NoSuchMethodException e) { return false; }
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Lifecycle annotation requires a no-arg method: {}
- Specified field type [{}] is incompatible with resource type
- Specified parameter type [{}] is incompatible with resource
- Cannot invoke instance method without factoryBeanName: {}
- No StringValueResolver specified - pass a resolver object in
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/a049b3b2daa77768.json.
Report an issue: GitHub.