{"record":{"id":"581868264b7d1161","repo":"quarkusio/quarkus","slug":"sub-resource-type-is-not-a-class-returntype-n","errorCode":null,"errorMessage":"Sub resource type is not a class: \" + returnType.name().toString()","messagePattern":"Sub resource type is not a class: \" \\+ returnType\\.name\\(\\)\\.toString\\(\\)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java","lineNumber":1608,"sourceCode":"        }\n\n        return result;\n    }\n\n    private void handleSubResourceMethod(List<JaxrsClientReactiveEnricherBuildItem> enrichers,\n            BuildProducer<GeneratedClassBuildItem> generatedClasses, ClassInfo interfaceClass, IndexView index,\n            String defaultMediaType, Map<DotName, String> httpAnnotationToMethod, String name,\n            ClassRestClientContext ownerContext, ResultHandle ownerTarget, int methodIndex,\n            ResourceMethod method, String[] javaMethodParameters, MethodInfo jandexMethod,\n            Set<ClassInfo> multipartResponseTypes, List<SubResourceParameter> ownerSubResourceParameters,\n            Map<GeneratedSubResourceKey, String> generatedSubResources, Map<String, Type> ownerIdentifierTypeLookupMap) {\n\n        // resolve type variables of the reurntype, mainly for the generatedSubResources cache\n        Type returnType = resolveType(jandexMethod.returnType(), ownerIdentifierTypeLookupMap, jandexMethod);\n\n        if (returnType.kind() != CLASS && returnType.kind() != PARAMETERIZED_TYPE) {\n            // sort of sub-resource method that returns a thing that isn't a class\n            throw new IllegalArgumentException(\"Sub resource type is not a class: \" + returnType.name().toString());\n        }\n\n        Map<DotName, Map<String, Type>> hierarchyIdentifierTypeLookupMap = buildhierarchyIdentifierTypeLookupMap(index, null,\n                returnType);\n\n        ClassInfo subInterface = index.getClassByName(returnType.name());\n        if (!Modifier.isInterface(subInterface.flags())) {\n            throw new IllegalArgumentException(\n                    \"Client interface method: \" + jandexMethod.declaringClass().name() + \"#\" + jandexMethod\n                            + \" has no HTTP method annotation  (@GET, @POST, etc) and it's return type: \"\n                            + returnType.name().toString() + \" is not an interface. \"\n                            + \"If it's a sub resource method, it has to return an interface. \"\n                            + \"If it's not, it has to have one of the HTTP method annotations.\");\n        }\n\n        ownerContext.createJavaMethodField(interfaceClass, jandexMethod, methodIndex);\n\n        List<SubResourceMethodParameterKeyPart> ownerSubResourceMethodParameters = new ArrayList<>();","sourceCodeStart":1590,"sourceCodeEnd":1626,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java#L1590-L1626","documentation":"When a REST client interface method has no HTTP method annotation, Quarkus treats it as a sub-resource method and inspects its return type. If that return type is neither a class nor a parameterized type (e.g. a primitive, array, or type variable), there is no interface to generate a sub-resource locator against, so generation fails.","triggerScenarios":"A client interface method without @GET/@POST/etc. whose return type is a primitive, array, type variable, or other non-class Type kind — e.g. `int getResource();` or `T[] items();` with no HTTP annotation.","commonSituations":"Typos where the HTTP annotation import is missing so the method silently becomes a sub-resource locator; generic type-variable returns erased/ unresolved; Kotlin or generated interfaces exposing non-class returns on un-annotated methods.","solutions":["Add the appropriate HTTP method annotation (@GET/@POST/...) to the client method so it is not treated as a sub-resource method","If it is a sub-resource method, change the return type to an interface (or parameterized interface)","Remove the offending method if it is not meant to be a client endpoint"],"exampleFix":"// before\n@Path(\"items\")\nint getItemsCount();\n// after\n@Path(\"items\")\n@GET\nlong getItemsCount();","handlingStrategy":"validation","validationCode":"import java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\n\nstatic void checkSubResourceReturnType(Class<?> iface) {\n    for (Method m : iface.getMethods()) {\n        if (m.isAnnotationPresent(jakarta.ws.rs.GET.class)\n            || m.isAnnotationPresent(jakarta.ws.rs.POST.class)\n            || m.isAnnotationPresent(jakarta.ws.rs.PUT.class)\n            || m.isAnnotationPresent(jakarta.ws.rs.DELETE.class)\n            || m.isAnnotationPresent(jakarta.ws.rs.PATCH.class)\n            || m.isAnnotationPresent(jakarta.ws.rs.HEAD.class)\n            || m.isAnnotationPresent(jakarta.ws.rs.OPTIONS.class)) continue;\n        Class<?> r = m.getReturnType();\n        if (!(r.isInterface())) {\n            throw new IllegalStateException(\n                m + \" has no HTTP annotation and returns non-interface \" + r);\n        }\n    }\n}","typeGuard":"static boolean isHttpAnnotatedOrInterfaceReturn(Method m) {\n    boolean http = m.getAnnotations().length > 0 && java.util.Arrays.stream(m.getAnnotations())\n        .anyMatch(a -> a.annotationType().getName().startsWith(\"jakarta.ws.rs.\"));\n    return http || m.getReturnType().isInterface();\n}","tryCatchPattern":null,"preventionTips":["Always annotate endpoint methods with an HTTP method annotation","Sub-resource methods must return interfaces, never classes or primitives","Check imports — a missing @GET import silently turns methods into sub-resource locators"],"tags":["quarkus","rest-client","sub-resource","jaxrs"],"backgroundTag":"invalid-sub-resource-return-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"}