quarkusio/quarkus · error · IllegalStateException

@CachedResults class must be an interface or declare a no-ar

Error message

@CachedResults class must be an interface or declare a no-args constructor: 

What it means

The class injected with @CachedResults must either be an interface (so the extension can generate a synthetic caching implementation) or a concrete class with a public no-args constructor. Any other shape cannot be proxied/generated and the build fails.

Source

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

        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)
                                && !a.name().equals(INJECT))
                        .toList(),
                exclude);
    }

    record CachedResultsInjectConfig(String cacheName, Long lockTimeout, DotName keyGenerator, ClassInfo injectedClazz,
            Collection<AnnotationInstance> annotations, String exclude) {

    }

    record MethodKey(String name, List<TypeEquivalenceKey> params, TypeEquivalenceKey returnType) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Make the injected type an interface and inject that.
  2. Add a public no-arg constructor to the concrete class and supply dependencies another way (setters/field injection).
  3. Move @CachedResults to a generated/accessor interface instead of caching the concrete class directly.

Example fix

// before
@ApplicationScoped
public class PriceService {
    public PriceService(Config c) { ... }
}

// after
public interface PriceService { Price get(String id); }

@Inject @CachedResults(cacheName = "prices")
PriceService priceService; // interface target
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = com.example.PriceService.class;
if (!c.isInterface() && java.lang.reflect.Modifier.isAbstract(c.getModifiers())) {
    throw new IllegalStateException("@CachedResults type must be an interface or concrete with no-arg ctor");
}
if (!c.isInterface() && c.getDeclaredConstructors().length > 0
        && java.util.Arrays.stream(c.getConstructors()).noneMatch(k -> k.getParameterCount() == 0)) {
    throw new IllegalStateException("@CachedResults concrete class needs a public no-arg constructor");
}

Type guard

static boolean isValidCachedResultsType(Class<?> c) {
    return c.isInterface() || java.util.Arrays.stream(c.getConstructors())
            .anyMatch(k -> k.getParameterCount() == 0);
}

Prevention

When it happens

Trigger: Annotating an injected concrete class that only has constructors taking arguments (no default constructor), or an abstract class without a no-arg constructor.

Common situations: Injecting a pre-existing service class with mandatory constructor dependencies and expecting @CachedResults to wrap it; migrating code from interface-based design to concrete classes.

Related errors


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