quarkusio/quarkus · error · IllegalStateException

Unsupported target:

Error message

Unsupported target:

What it means

During Quarkus build, the cache extension processes every @CachedResults annotation. @CachedResults is only allowed on an injection field or an injection method parameter; any other annotation target (e.g. a class, method, or type-use) is rejected. This is a build-time deployment error, not a runtime failure.

Source

Thrown at extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CachedResultsProcessor.java:344

        AnnotationValue lockTimeoutValue = annotation.value("lockTimeout");
        if (lockTimeoutValue != null)
            lockTimeout = lockTimeoutValue.asLong();

        AnnotationValue keyGeneratorValue = annotation.value("keyGenerator");
        if (keyGeneratorValue != null)
            keyGenerator = keyGeneratorValue.asClass().name();

        AnnotationValue excludeValue = annotation.value("exclude");
        if (excludeValue != null)
            exclude = excludeValue.asString();

        if (annotation.target().kind() == Kind.FIELD) {
            type = annotation.target().asField().type();
        } else if (annotation.target().kind() == Kind.METHOD_PARAMETER) {
            type = annotation.target().asMethodParameter().type();
        } else {
            throw new IllegalStateException("Unsupported target:" + annotation.target());
        }

        if (type.kind() != Type.Kind.CLASS) {
            throw new IllegalStateException("Invalid type: " + type);
        }
        ClassInfo injectedClazz = index.getClassByName(type.name());
        if (injectedClazz == null) {
            throw new IllegalStateException("Injected class not found in index: " + type.name());
        }
        if (!injectedClazz.isInterface()
                && !injectedClazz.hasNoArgsConstructor()) {
            throw new IllegalStateException(
                    "@CachedResults class must be an interface or declare a no-args constructor: " + injectedClazz);
        }
        return new CachedResultsInjectConfig(cacheName, lockTimeout, keyGenerator, injectedClazz,
                // consider all but @CachedResults and @Inject
                annotation.target().declaredAnnotations().stream().filter(
                        a -> !a.name().equals(CACHED_RESULTS)

View on GitHub (pinned to e1c734241f)

Solutions

  1. Move @CachedResults onto the injected field: `@Inject @CachedResults MyInterface result;`
  2. If injected via constructor/setter, put it on the parameter: `public MyClass(@CachedResults MyInterface r) {...}`
  3. Never place @CachedResults on a class, plain (non-injection) method, or local variable.

Example fix

// before
@CachedResults
public class MyService { ... }

// after
public class MyService {
    @Inject
    @CachedResults(cacheName = "my-cache")
    MyResultInterface result;
}
Defensive patterns

Strategy: validation

Validate before calling

// Build-time check: only fields or method parameters of an injected point may carry @CachedResults
// In application code, simply never place @CachedResults elsewhere; verify usage:
// grep -rn --include='*.java' -B2 '@CachedResults' src/main/java | grep -E 'class |void |static '

Prevention

When it happens

Trigger: Placing @CachedResults on a target other than a FIELD or METHOD_PARAMETER — e.g. annotating the class itself, a business method, or a local variable while the deployment step walks injection points and calls createConfig.

Common situations: Developers copying @Inject-style annotation placement assumptions, annotating a constructor or setter instead of a field, or IDE quick-fixes moving the annotation to the wrong element.

Related errors


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