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
- Convert the repository to a Java interface extending a Spring Data interface (e.g., JpaRepository)
- Remove repository stereotypes from concrete classes; keep them only on interfaces
- If it's a custom base repository, define it as an interface and implement defaults elsewhere
- 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
- Declare repositories as interfaces extending Spring Data interfaces
- Never put repository stereotypes on concrete classes
- Code-review repository declarations after Spring migrations
- Use an ArchUnit rule: classes annotated with @Repository must be interfaces (except JPA exception-translation usage)
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
- ${classInfo.name()} has to be an interface
- ${classInfo.name()} should only extend REST Data Panache int
- ${classInfo.name()} should not be extended or implemented
- Couldn't find id field of ${classInfo}
- Path parameter may not be followed by an alphanumeric chara
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/c6ab835da98bec87.
Report an issue: GitHub.