quarkusio/quarkus · error · RuntimeException
has to be an interface
Error message
has to be an interface
What it means
REST Data Panache resources in Quarkus must be Java interfaces; the processor scans for annotated candidates and validates each. A concrete class annotated as a REST Data Panache resource cannot be used to generate data-backed endpoints, so validateResource throws.
Source
Thrown at extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/HibernateReactivePanacheRestProcessor.java:131
String idType = generics.get(2).name().toString();
List<ClassInfo> listenersForEntityType = getListenersByEntityType(index, resourceMethodListeners, entityType);
DataAccessImplementor dataAccessImplementor = new RepositoryDataAccessImplementor(repositoryClassName);
String resourceClass = resourceImplementor.implement(
classOutput, dataAccessImplementor, resourceInterface, entityType, listenersForEntityType);
// Make sure that repository bean is not removed and will be injected to the generated resource
unremovableBeansProducer.produce(new UnremovableBeanBuildItem(
new UnremovableBeanBuildItem.BeanClassNameExclusion(repositoryClassName)));
restDataResourceProducer.produce(new RestDataResourceBuildItem(
new ResourceMetadata(resourceClass, resourceInterface, entityType, idType,
getEntityFields(index, entityType))));
}
}
private void validateResource(IndexView index, ClassInfo classInfo) {
if (!Modifier.isInterface(classInfo.flags())) {
throw new RuntimeException(classInfo.name() + " has to be an interface");
}
if (classInfo.interfaceNames().size() > 1) {
throw new RuntimeException(classInfo.name() + " should only extend REST Data Panache interface");
}
if (!index.getKnownDirectImplementors(classInfo.name()).isEmpty()) {
throw new RuntimeException(classInfo.name() + " should not be extended or implemented");
}
}
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 annotated type into an interface extending the appropriate REST Data Panache interface
- Remove the annotation from the class and implement endpoints manually
- Move the annotation to a new interface that targets the entity or repository
Example fix
// before
@PanacheEntityResource
public class CustomerResource {}
// after
@PanacheEntityResource
public interface CustomerResource extends PanacheMongoResource<Customer> {} Defensive patterns
Strategy: validation
Validate before calling
if (!CustomerResource.class.isInterface()) throw new IllegalStateException("REST Data Panache resource must be an interface"); Prevention
- Never put REST Data Panache annotations on classes
- Kotlin: use 'interface', not 'class'
- Check docs for the annotation's @Target
When it happens
Trigger: A class (not an interface) carries a REST Data Panache resource annotation, found via findEntityResources or findRepositoryResources during augmentation.
Common situations: Applying the resource annotation to a class instead of an interface; Kotlin 'class' vs 'interface' mistake; misunderstanding that REST Data Panache only generates implementations of interfaces.
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
- should only extend REST Data Panache interface
- Couldn't find id field of ${classInfo}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/eb8d0eb7e4cd50fc.
Report an issue: GitHub.