quarkusio/quarkus · error · RuntimeException

has to be an interface

Error message

 has to be an interface

What it means

The MongoPanacheRestProcessor validates at build time that each detected REST data resource is an interface. Passing a concrete class (annotated to be treated as a resource) throws RuntimeException '<class> has to be an interface'.

Source

Thrown at extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java:138

            DataAccessImplementor dataAccessImplementor = new RepositoryDataAccessImplementor(repositoryClassName);
            String resourceClass = resourceImplementor.implement(
                    classOutput, dataAccessImplementor, resourceInterface, entityType);
            unremovableBeansProducer.produce(new UnremovableBeanBuildItem(
                    new UnremovableBeanBuildItem.BeanClassNameExclusion(repositoryClassName)));

            restDataResourceProducer.produce(new RestDataResourceBuildItem(
                    new ResourceMetadata(resourceClass, resourceInterface, entityType, idType,
                            getEntityFields(index.getIndex(), entityType))));
            if (capabilities.isPresent(Capability.RESTEASY)) {
                bytecodeTransformersProducer.produce(
                        getEntityIdAnnotationTransformer(entityType, entityClassHelper.getIdField(entityType).name()));
            }
        }
    }

    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

  1. Convert the resource to an interface that extends the appropriate REST Data Panache interface (e.g. PanacheMongoRepositoryResource).
  2. Move the annotation from the entity class to a dedicated resource interface.
  3. Remove the annotation if a REST resource was not intended.

Example fix

// before
@PanacheMongoResource
public class BookResource { ... } // concrete class
// after
@PanacheMongoResource
public interface BookResource extends PanacheMongoRepositoryResource<Book, ObjectId> {}
Defensive patterns

Strategy: validation

Validate before calling

if (!resource.isInterface()) throw new IllegalArgumentException("@PanacheMongoResource target must be an interface: " + resource.getName());

Prevention

When it happens

Trigger: Annotating a concrete class (not an interface) with the REST Data Panache resource annotation so the build-step processor picks it up in findEntityResources/findRepositoryResources.

Common situations: Misreading the REST Data Panache model: resources must be interfaces extending PanacheMongoEntityBase-style resource contracts, while the entity itself is a separate class.

Related errors


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