{"record":{"id":"efdfb60348c2c299","repo":"quarkusio/quarkus","slug":"method-of-repository-repository-can-only-use","errorCode":null,"errorMessage":"${method} of Repository ${repository} can only use interfaces to map results to non-entity types.","messagePattern":"(.+?) of Repository (.+?) can only use interfaces to map results to non-entity types\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java","lineNumber":276,"sourceCode":"                            customResultTypeName = resultType.name();\n\n                            if (customResultTypeName.equals(entityClassInfo.name())\n                                    || isHibernateSupportedReturnType(customResultTypeName)) {\n                                // no special handling needed\n                                customResultTypeName = null;\n                            } else {\n                                // If the custom type is an interface, we need to generate the implementation\n                                ClassInfo resultClassInfo = index.getClassByName(customResultTypeName);\n                                if (Modifier.isInterface(resultClassInfo.flags())) {\n                                    // Find the implementation name, and use that for subsequent query result generation\n                                    customResultTypeName = customResultTypeImplNames.computeIfAbsent(customResultTypeName,\n                                            k -> createSimpleInterfaceImpl(k, entityClassInfo.name()));\n\n                                    // Remember the parameters for this usage of the custom type, we'll deal with it later\n                                    customResultTypes.computeIfAbsent(customResultTypeName,\n                                            k -> new ArrayList<>()).add(method.name());\n                                } else {\n                                    throw new IllegalArgumentException(\n                                            method.name() + \" of Repository \" + repositoryClassInfo\n                                                    + \" can only use interfaces to map results to non-entity types.\");\n                                }\n                            }\n                        }\n\n                        DotName effectiveReturnTypeName = finalReturnType.kind() == Type.Kind.TYPE_VARIABLE ? DotNames.OBJECT\n                                : finalReturnType.name();\n\n                        generateFindQueryResultHandling(bc, panacheQuery, finalPageableParameterIndex, params,\n                                repositoryClassInfo, entityClassInfo, effectiveReturnTypeName, parseResult.getTopCount(),\n                                method.name(), customResultTypeName,\n                                entityClassInfo.name().toString(), elementTypeToCast);\n\n                    } else if (parseResult.getQueryType() == MethodNameParser.QueryType.COUNT) {\n                        if (!DotNames.PRIMITIVE_LONG.equals(returnType.name())\n                                && !DotNames.LONG.equals(returnType.name())) {\n                            throw new IllegalArgumentException(","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java#L258-L294","documentation":"When a derived query's return type is not the entity type, Quarkus's spring-data-jpa extension maps results into that type at build time by generating an implementation — but only if the type is an interface (an interface projection backed by entity getters). Returning a concrete non-entity class cannot be supported by generated bytecode, so the build fails with this IllegalArgumentException.","triggerScenarios":"Declaring e.g. PersonDto findByEmail(String email) where PersonDto is a concrete class (not the entity Person and not an interface), inside a method whose query type is SELECT and whose custom result type is not an interface.","commonSituations":"Creating a DTO class with fields copied from the entity and using it directly as a return type; migrating from another framework (e.g. JPA constructor-expression queries) where class-based projections are allowed.","solutions":["Convert the DTO class to an interface with getters matching the entity's properties (interface projection)","Use the entity type as the return type and map to a DTO manually in application code","Use a @Query with a constructor/SELECT projection if a concrete class is really required"],"exampleFix":"// before\nclass PersonDto { private String name; public String getName() { return name; } }\nPersonDto findByEmail(String email);\n\n// after\ninterface PersonNameView { String getName(); }\nPersonNameView findByEmail(String email);","handlingStrategy":"validation","validationCode":"// Ensure custom result types used in repository methods are interfaces\nstatic void checkProjectionTypes(Class<?> repo) {\n    for (Method m : repo.getDeclaredMethods()) {\n        Class<?> r = m.getReturnType();\n        if (!r.isInterface() && !r.isAnnotationPresent(Entity.class) && !isBuiltIn(r))\n            throw new IllegalStateException(m.getName() + \" returns non-interface non-entity \" + r);\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model projections as interfaces with getters matching entity properties","Never return concrete DTO classes from derived query methods","Name projection interfaces with a View/Projection suffix to make intent explicit"],"tags":["spring-data","jpa","quarkus","projection","build-time"],"backgroundTag":"invalid-projection-type","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}