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

  1. Ensure the custom value annotation declares a String value() default ...; attribute (it is read by the literal name 'value').
  2. If renaming an attribute, also keep/alias a 'value' member or stop registering it as the valueAnnotationType.
  3. 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

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


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/a049b3b2daa77768.json. Report an issue: GitHub.