quarkusio/quarkus · error · RuntimeException

${class} has to be an interface

Error message

${class} has to be an interface

What it means

Spring Data REST in Quarkus only supports repository interfaces; the extension validates every candidate resource/repository class before generating implementations. If the class selected as a resource (e.g., annotated or picked up as a Spring Data repository) is a concrete class or enum rather than an interface, the build fails with this RuntimeException.

Source

Thrown at extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java:163

        Set<ClassInfo> result = new LinkedHashSet<>();
        for (DotName repositoryInterface : repositoryInterfaces) {
            for (ClassInfo classInfo : indexView.getKnownDirectImplementors(repositoryInterface)) {
                if (!hasImplementors(indexView, classInfo) && !EXCLUDED_INTERFACES.contains(classInfo.name())) {
                    validateResource(classInfo);
                    result.add(classInfo);
                }
            }
        }
        return result;
    }

    private boolean hasImplementors(IndexView index, ClassInfo classInfo) {
        return !index.getKnownDirectImplementors(classInfo.name()).isEmpty();
    }

    private void validateResource(ClassInfo classInfo) {
        if (!Modifier.isInterface(classInfo.flags())) {
            throw new RuntimeException(classInfo.name() + " has to be an interface");
        }
    }

    private List<Type> getGenericTypes(ClassInfo classInfo) {
        return classInfo.interfaceTypes()
                .stream()
                .findFirst()
                .orElseThrow(() -> new RuntimeException(classInfo.toString() + " does not have generic types"))
                .asParameterizedType()
                .arguments();
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Convert the repository to a Java interface extending a Spring Data interface (e.g., JpaRepository)
  2. Remove repository stereotypes from concrete classes; keep them only on interfaces
  3. If it's a custom base repository, define it as an interface and implement defaults elsewhere
  4. Check for accidental class/interface mix-up when refactoring

Example fix

// before
public class UserRepo extends JpaRepository<User, Long> {} // class

// after
public interface UserRepo extends JpaRepository<User, Long> {}
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time guard:
// interfaces only:
if (!UserRepo.class.isInterface()) {
    throw new IllegalStateException("Spring Data repositories must be interfaces");
}

Prevention

When it happens

Trigger: A class (not interface) is picked up by getRepositoriesToImplement — typically a concrete class accidentally annotated/registered as a Spring Data repository, or a base class extended instead of an interface in the repository hierarchy.

Common situations: Migrating Spring code where a repository was a class; accidentally annotating an entity/service with a repository stereotype; generics causing the processor to select the wrong class from the index.

Related errors


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