quarkusio/quarkus · error · IllegalArgumentException
Currently Spring Cache annotations can only be added to meth
Error message
Currently Spring Cache annotations can only be added to methods. Offending instance is annotation ' + instance + ' on + instance.target() + '
What it means
Quarkus's Spring Cache extension processes @Cacheable/@CacheEvict/@CachePut annotations at build time and only supports method-level placement, unlike Spring which also honors class-level annotations. During deployment, validateUsage inspects each annotation instance and throws when its Jandex target kind is not METHOD. The build fails immediately so misannotated code is caught before runtime.
Source
Thrown at extensions/spring-cache/deployment/src/main/java/io/quarkus/spring/cache/SpringCacheProcessor.java:75
Collection<AnnotationInstance> instances = combinedIndex.getIndex().getAnnotations(cacheAnnotation);
for (AnnotationInstance instance : instances) {
validateUsage(instance);
Optional<String> cacheName = getSpringCacheName(instance);
if (cacheName.isPresent()) {
cacheNames.add(cacheName.get());
}
}
}
List<AdditionalCacheNameBuildItem> result = new ArrayList<>(cacheNames.size());
for (String cacheName : cacheNames) {
result.add(new AdditionalCacheNameBuildItem(cacheName));
}
return result;
}
private void validateUsage(AnnotationInstance instance) {
if (instance.target().kind() != AnnotationTarget.Kind.METHOD) {
throw new IllegalArgumentException(
"Currently Spring Cache annotations can only be added to methods. Offending instance is annotation '"
+ instance + "' on " + instance.target() + "'");
}
List<AnnotationValue> values = instance.values();
List<String> unsupportedValues = new ArrayList<>();
for (AnnotationValue value : values) {
if (CURRENTLY_UNSUPPORTED_ANNOTATION_VALUES.contains(value.name())) {
unsupportedValues.add(value.name());
}
}
if (!unsupportedValues.isEmpty()) {
throw new IllegalArgumentException("Annotation '" +
instance + "' on '" + instance.target()
+ "' contains the following currently unsupported annotation values: "
+ String.join(", ", unsupportedValues));
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- Move the annotation from the class/field onto each concrete public method you want cached
- If you intended class-wide caching, annotate every relevant method explicitly
- Remove the annotation from interfaces/parameters entirely
Example fix
// before
@Cacheable("items")
public class ItemService { ... }
// after
public class ItemService {
@Cacheable("items")
public Item find(long id) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
if (!method.isAnnotationPresent(Cacheable.class)) return;
// ensure annotation only on methods:
if (this.getClass().getAnnotation(Cacheable.class) != null)
throw new IllegalStateException("@Cacheable must be on methods, not the class"); Prevention
- Never place @Cacheable/@CachePut/@CacheEvict on classes or interfaces in Quarkus
- Annotate concrete bean methods only
- Check the Quarkus Spring Cache guide for supported attributes before porting Spring code
When it happens
Trigger: Placing @Cacheable, @CachePut, or @CacheEvict on a class, interface, field, or parameter instead of a method in a Quarkus application; the annotation is then indexed as a non-METHOD AnnotationTarget and cacheNames() -> validateUsage() throws during augmentation.
Common situations: Porting a Spring Boot app where class-level @Cacheable was legal; developers copying Spring examples that annotate the class to apply caching to all methods.
Related errors
- Annotation '${instance}' on '${instance.target()}' contains
- Quarkus currently only supports using a single cache name. O
- Failed to open path tree with root %s
- Dev services for ${request.getName()} requires a startable s
- Name cannot start with '/':${name}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/e9406e769b087b91.
Report an issue: GitHub.