{"record":{"id":"5cf5bd3f8aff99ae","repo":"quarkusio/quarkus","slug":"client-interface-method-jandexmethod-declarin","errorCode":null,"errorMessage":"Client interface method: \" + jandexMethod.declaringClass().name() + \"#\" + jandexMethod + \" has no HTTP method annotation  (@GET, @POST, etc) and it's return type: \" + returnType.name().toString() + \" is not an interface. If it's a sub resource method, it has to return an interface. If it's not, it has to have one of the HTTP method annotations.","messagePattern":"Client interface method: \" \\+ jandexMethod\\.declaringClass\\(\\)\\.name\\(\\) \\+ \"#\" \\+ jandexMethod \\+ \" has no HTTP method annotation  \\(@GET, @POST, etc\\) and it's return type: \" \\+ returnType\\.name\\(\\)\\.toString\\(\\) \\+ \" is not an interface\\. If it's a sub resource method, it has to return an interface\\. If it's not, it has to have one of the HTTP method annotations\\.","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":1616,"sourceCode":"            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<>();\n        for (SubResourceParameter ownerSubResourceParameter : ownerSubResourceParameters) {\n            ownerSubResourceMethodParameters.add(SubResourceMethodParameterKeyPart.of(ownerSubResourceParameter));\n        }\n\n        // method parameters (except path parameters) are rewritten to sub client fields (directly, public fields):\n        List<SubResourceMethodParameterKeyPart> subResourceMethodParameters = new ArrayList<>();\n        MethodParameter[] parameters = method.getParameters();\n        for (int i = 0; i < parameters.length; i++) {","sourceCodeStart":1598,"sourceCodeEnd":1634,"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#L1598-L1634","documentation":"A client interface method without an HTTP method annotation is interpreted as a sub-resource method, which must return an interface so Quarkus can generate a client implementation for it. If the return type resolves to a concrete class, generation cannot proceed and this error is thrown.","triggerScenarios":"Calling a method like `@Path(\"sub\") MyServiceImpl getSub();` on a @RegisterRestClient interface — no @GET/@POST annotation and a class (not interface) return type.","commonSituations":"Returning a concrete implementation class instead of the JAX-RS interface; forgetting an HTTP annotation on what was meant to be an endpoint; migrating a server-side resource class to the client side where sub-resources must be interfaces.","solutions":["If it's a sub-resource method, change the return type to an interface","If it's an endpoint, add an HTTP method annotation (@GET, @POST, etc.)","Split the class into an interface + implementation and reference the interface in the client"],"exampleFix":"// before\n@Path(\"sub\")\nSubResourceImpl getSubResource();\n// after\n@Path(\"sub\")\nSubResource getSubResource(); // SubResource is an interface","handlingStrategy":"validation","validationCode":"static void requireInterfaceReturn(Class<?> client, String method) throws Exception {\n    Method m = client.getMethod(method);\n    if (!m.getReturnType().isInterface()) {\n        throw new IllegalStateException(\n            client.getName() + \"#\" + method + \" must return an interface (sub-resource) or carry an HTTP annotation\");\n    }\n}","typeGuard":"static boolean validClientMethod(Method m) {\n    boolean hasHttp = java.util.Arrays.stream(m.getAnnotations())\n        .anyMatch(a -> a.annotationType().getName().startsWith(\"jakarta.ws.rs.\"));\n    return hasHttp || m.getReturnType().isInterface();\n}","tryCatchPattern":null,"preventionTips":["Program against interfaces in client APIs — return the interface, not the impl","Verify every non-annotated method on a @RegisterRestClient is an intentional sub-resource locator","Document team convention: client interfaces only expose interface-typed sub-resources"],"tags":["quarkus","rest-client","sub-resource","jaxrs"],"backgroundTag":"sub-resource-must-return-interface","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}