{"record":{"id":"38a8d445ecb032f7","repo":"quarkusio/quarkus","slug":"resource-locator-method-returned-object-that-was-n","errorCode":null,"errorMessage":"Resource locator method returned object that was not a resource: ","messagePattern":"Resource locator method returned object that was not a resource: ","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ResourceLocatorHandler.java","lineNumber":80,"sourceCode":"            locatorClass = locator.getClass();\n        }\n\n        // in case of a subresource gets returned, we might not control the lifecycle of the subresource ourself\n        // E.g. the user could return a singleton instance, or construct an instance on each invocation of the locator.\n        // therefore, only inject into CDI Beans, where we already know they are constructed once for each request\n        // (thanks to the requestScopedResources validation)\n        // otherwise TCK JAXRSClient0015 fails\n        Object unwrapped = null;\n        if (clientProxyUnwrapper != null) {\n            unwrapped = clientProxyUnwrapper.apply(locator);\n        }\n        if (unwrapped instanceof ResteasyReactiveInjectionTarget t && unwrapped != locator) {\n            t.__quarkus_rest_inject(requestContext);\n        }\n\n        Map<String, RequestMapper<RuntimeResource>> target = findTarget(locatorClass);\n        if (target == null) {\n            throw new RuntimeException(\"Resource locator method returned object that was not a resource: \" + locator);\n        }\n\n        RequestMapper<RuntimeResource> mapper = target.get(requestContext.getMethod());\n        RequestMapper.RequestMatch<RuntimeResource> res;\n        boolean hadNullMethodMapper;\n        if (mapper != null) {\n            res = findRequestMatch(mapper, requestContext);\n            hadNullMethodMapper = false;\n        } else {\n            res = findRequestMatch(target.get(null), requestContext); //another layer of resource locators maybe\n            // we set this without checking if we matched, but we only use it after\n            // we check for a null mapper, so by the time we use it, it must have meant that\n            // we had a matcher for a null method\n            hadNullMethodMapper = true;\n\n            if (res == null) {\n                String requestMethod = requestContext.getMethod();\n                if (requestMethod.equals(HttpMethod.HEAD)) {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ResourceLocatorHandler.java#L62-L98","documentation":"ResourceLocatorHandler follows a resource locator method's return value to sub-resource classes. After unwrapping and injecting the returned object, it looks up routing targets by the object's class; if the class has no registered runtime resources, the returned object is not a recognized sub-resource and a RuntimeException is thrown naming the locator object.","triggerScenarios":"A @Path method with no HTTP method annotation (locator) returns an object whose class was not itself scanned as a resource class (no @Path on the class, or not returned by a locator-registered class); returning a proxy, null-wrapped, or non-resource instance.","commonSituations":"Returning an instance of a class missing @Path; returning a class produced at runtime or from another module not indexed as a JAX-RS resource; typos where the locator returns a DTO instead of the sub-resource.","solutions":["Annotate the returned class with @Path and give it HTTP method methods so it registers as a sub-resource class","Return an instance of the correct sub-resource class from the locator","Ensure the sub-resource class is in an indexed package of the application","Verify the locator method has no @GET/@POST annotation (it must be a locator, not an endpoint)"],"exampleFix":"// before\nclass Sub { public String get() { return \"x\"; } } // no @Path\n@Path(\"/root\")\nclass Root {\n  @Path(\"/sub\")\n  public Sub sub() { return new Sub(); }\n}\n// after\n@Path(\"/sub\")\nclass Sub {\n  @GET public String get() { return \"x\"; }\n}","handlingStrategy":"validation","validationCode":"Object sub = locator();\nif (sub == null || sub.getClass().getAnnotation(jakarta.ws.rs.Path.class) == null) {\n    throw new IllegalStateException(\"Locator must return a @Path-annotated resource class\");\n}","typeGuard":"static boolean isSubResource(Object o) {\n    return o != null && o.getClass().isAnnotationPresent(jakarta.ws.rs.Path.class);\n}","tryCatchPattern":"try { return locator.locate(); } catch (RuntimeException e) { if (e.getMessage().startsWith(\"Resource locator method returned object\")) { /* return correct @Path class instance */ } throw e; }","preventionTips":["Ensure every locator-returned class has @Path and HTTP method annotations","Never return DTOs or proxies from locator methods","Keep locator methods free of @GET/@POST annotations"],"tags":["resteasy-reactive","subresource-locator","routing","jaxrs"],"backgroundTag":"invalid-subresource-locator-result","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"}