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

  1. Move the annotation from the class/field onto each concrete public method you want cached
  2. If you intended class-wide caching, annotate every relevant method explicitly
  3. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/e9406e769b087b91. Report an issue: GitHub.