quarkusio/quarkus · error · RuntimeException
${classInfo.name()} has to be an interface
Error message
${classInfo.name()} has to be an interface What it means
The REST Data Panache processor validates each discovered resource class and requires it to be a Java interface; a class bound to REST Data Panache is rejected at build time with this RuntimeException.
Source
Thrown at extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java:149
List<ClassInfo> listenersForEntityType = getListenersByEntityType(index.getIndex(), 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.getIndex(), 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 resource from a class to an interface that extends PanacheEntityResource or PanacheRepositoryResource
- Remove the class from REST Data Panache scanning if it was never meant to be a resource
- Check imports/annotations so only the interface is registered
Example fix
// before
public class PersonResource implements PanacheEntityResource<Person, Long> {}
// after
public interface PersonResource extends PanacheEntityResource<Person, Long> {} Defensive patterns
Strategy: validation
Validate before calling
if (!resourceClass.isInterface())
throw new IllegalStateException(resourceClass + " must be an interface extending a Panache REST Data resource"); Type guard
static boolean isResourceInterface(Class<?> c) {
return c.isInterface();
} Prevention
- Declare resources as 'interface X extends PanacheEntityResource<...>' only
- Never name resource implementations with class keyword
- Keep concrete REST resources (JAX-RS) separate from REST Data Panache interfaces
When it happens
Trigger: Annotating a concrete class with @ResourceClass or having the processor pick up a class (not interface) named/registered as an entity or repository resource in HibernateOrmPanacheRestProcessor.findEntityResources/findRepositoryResources.
Common situations: Typo: writing 'class MyResource implements ...' instead of 'interface MyResource extends ...'; copying a repository implementation into the scanned package.
Related errors
- ${classInfo.name()} should only extend REST Data Panache int
- ${classInfo.name()} should not be extended or implemented
- Couldn't find id field of ${classInfo}
- has to be an interface
- should only extend REST Data Panache interface
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/7c818ced09523f50.
Report an issue: GitHub.