quarkusio/quarkus · error · RuntimeException

should only extend REST Data Panache interface

Error message

 should only extend REST Data Panache interface

What it means

A REST Data Panache resource interface may only extend a single REST Data Panache interface (e.g. PanacheMongoRepositoryResource); extending anything else would make the generated method set ambiguous. The processor enforces this by counting interfaceNames.

Solutions

  1. Keep the annotated resource interface extending exactly one REST Data Panache interface
  2. Move any extra interface inheritance to a repository or service class instead
  3. Add custom endpoints via separate CDI beans/JAX-RS resources rather than extra interfaces

Example fix

// before
@PanacheRepositoryResource
public interface CustomerResource extends PanacheMongoRepositoryResource<CustomerRepository, Customer, Long>, Serializable {}
// after
@PanacheRepositoryResource
public interface CustomerResource extends PanacheMongoRepositoryResource<CustomerRepository, Customer, Long> {}
Defensive patterns

Strategy: validation

Validate before calling

if (Arrays.stream(CustomerResource.class.getInterfaces()).count() != 1)
    throw new IllegalStateException("Resource interface must extend exactly one REST Data Panache interface");

Prevention

When it happens

Trigger: An interface annotated with a REST Data Panache annotation extends more than one interface (e.g. both a Panache resource interface and another unrelated interface).

Common situations: Adding a custom interface to share extra method contracts with resource interfaces; refactoring that merged resource interfaces with application interfaces.

Related errors


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

Appendix: source

Thrown at extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/HibernateReactivePanacheRestProcessor.java:135

            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)