quarkusio/quarkus · error · RuntimeException
Resource locator method returned object that was not a resou
Error message
Resource locator method returned object that was not a resource:
What it means
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.
Source
Thrown at independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ResourceLocatorHandler.java:80
locatorClass = locator.getClass();
}
// in case of a subresource gets returned, we might not control the lifecycle of the subresource ourself
// E.g. the user could return a singleton instance, or construct an instance on each invocation of the locator.
// therefore, only inject into CDI Beans, where we already know they are constructed once for each request
// (thanks to the requestScopedResources validation)
// otherwise TCK JAXRSClient0015 fails
Object unwrapped = null;
if (clientProxyUnwrapper != null) {
unwrapped = clientProxyUnwrapper.apply(locator);
}
if (unwrapped instanceof ResteasyReactiveInjectionTarget t && unwrapped != locator) {
t.__quarkus_rest_inject(requestContext);
}
Map<String, RequestMapper<RuntimeResource>> target = findTarget(locatorClass);
if (target == null) {
throw new RuntimeException("Resource locator method returned object that was not a resource: " + locator);
}
RequestMapper<RuntimeResource> mapper = target.get(requestContext.getMethod());
RequestMapper.RequestMatch<RuntimeResource> res;
boolean hadNullMethodMapper;
if (mapper != null) {
res = findRequestMatch(mapper, requestContext);
hadNullMethodMapper = false;
} else {
res = findRequestMatch(target.get(null), requestContext); //another layer of resource locators maybe
// we set this without checking if we matched, but we only use it after
// we check for a null mapper, so by the time we use it, it must have meant that
// we had a matcher for a null method
hadNullMethodMapper = true;
if (res == null) {
String requestMethod = requestContext.getMethod();
if (requestMethod.equals(HttpMethod.HEAD)) {View on GitHub (pinned to e1c734241f)
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)
Example fix
// before
class Sub { public String get() { return "x"; } } // no @Path
@Path("/root")
class Root {
@Path("/sub")
public Sub sub() { return new Sub(); }
}
// after
@Path("/sub")
class Sub {
@GET public String get() { return "x"; }
} Defensive patterns
Strategy: validation
Validate before calling
Object sub = locator();
if (sub == null || sub.getClass().getAnnotation(jakarta.ws.rs.Path.class) == null) {
throw new IllegalStateException("Locator must return a @Path-annotated resource class");
} Type guard
static boolean isSubResource(Object o) {
return o != null && o.getClass().isAnnotationPresent(jakarta.ws.rs.Path.class);
} Try / catch
try { return locator.locate(); } catch (RuntimeException e) { if (e.getMessage().startsWith("Resource locator method returned object")) { /* return correct @Path class instance */ } throw e; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ${exposedEndpoint} is declared by :${endpointConfigs}
- retrieving all roles not supported when JAX-RS security cont
- More than one Application class: ${applications}
- '@Inject' cannot be used with a '${ResteasyReactiveDotNames.
- Illegal URI template${uriTemplate}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/38a8d445ecb032f7.
Report an issue: GitHub.