{"record":{"id":"7c818ced09523f50","repo":"quarkusio/quarkus","slug":"classinfo-name-has-to-be-an-interface","errorCode":null,"errorMessage":"${classInfo.name()} has to be an interface","messagePattern":"(.+?) has to be an interface","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java","lineNumber":149,"sourceCode":"\n            List<ClassInfo> listenersForEntityType = getListenersByEntityType(index.getIndex(), resourceMethodListeners,\n                    entityType);\n            DataAccessImplementor dataAccessImplementor = new RepositoryDataAccessImplementor(repositoryClassName);\n            String resourceClass = resourceImplementor.implement(\n                    classOutput, dataAccessImplementor, resourceInterface, entityType, listenersForEntityType);\n            // Make sure that repository bean is not removed and will be injected to the generated resource\n            unremovableBeansProducer.produce(new UnremovableBeanBuildItem(\n                    new UnremovableBeanBuildItem.BeanClassNameExclusion(repositoryClassName)));\n\n            restDataResourceProducer.produce(new RestDataResourceBuildItem(\n                    new ResourceMetadata(resourceClass, resourceInterface, entityType, idType,\n                            getEntityFields(index.getIndex(), entityType))));\n        }\n    }\n\n    private void validateResource(IndexView index, ClassInfo classInfo) {\n        if (!Modifier.isInterface(classInfo.flags())) {\n            throw new RuntimeException(classInfo.name() + \" has to be an interface\");\n        }\n\n        if (classInfo.interfaceNames().size() > 1) {\n            throw new RuntimeException(classInfo.name() + \" should only extend REST Data Panache interface\");\n        }\n\n        if (!index.getKnownDirectImplementors(classInfo.name()).isEmpty()) {\n            throw new RuntimeException(classInfo.name() + \" should not be extended or implemented\");\n        }\n    }\n\n    private List<Type> getGenericTypes(ClassInfo classInfo) {\n        return classInfo.interfaceTypes()\n                .stream()\n                .findFirst()\n                .orElseThrow(() -> new RuntimeException(classInfo.toString() + \" does not have generic types\"))\n                .asParameterizedType()\n                .arguments();","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java#L131-L167","documentation":"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.","triggerScenarios":"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.","commonSituations":"Typo: writing 'class MyResource implements ...' instead of 'interface MyResource extends ...'; copying a repository implementation into the scanned package.","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"],"exampleFix":"// before\npublic class PersonResource implements PanacheEntityResource<Person, Long> {}\n// after\npublic interface PersonResource extends PanacheEntityResource<Person, Long> {}","handlingStrategy":"validation","validationCode":"if (!resourceClass.isInterface())\n    throw new IllegalStateException(resourceClass + \" must be an interface extending a Panache REST Data resource\");","typeGuard":"static boolean isResourceInterface(Class<?> c) {\n    return c.isInterface();\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["quarkus","panache","rest-data","build-time","interface"],"backgroundTag":"resource-must-be-interface","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}