quarkusio/quarkus · error · IllegalStateException
@ClientExceptionMapper is only supported on static methods o
Error message
@ClientExceptionMapper is only supported on static methods of REST Client interfaces that take 'jakarta.ws.rs.core.Response' as a single parameter and return 'java.lang.RuntimeException'. Offending instance is '${className}#${methodName}' What it means
@ClientExceptionMapper marks a static method in a REST client interface that maps a failed Response to a RuntimeException. Quarkus enforces a fixed signature: exactly one parameter of type jakarta.ws.rs.core.Response and a return type of java.lang.RuntimeException (or subclass). This IllegalStateException is thrown at build time when the annotated method deviates from that contract.
Source
Thrown at extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/ClientExceptionMapperHandler.java:102
try {
boolean returnsRuntimeException = RuntimeException.class.isAssignableFrom(
Class.forName(returnTypeClassName, false, Thread.currentThread().getContextClassLoader()));
if (returnsRuntimeException) {
isValid = true;
}
} catch (ClassNotFoundException ignored) {
}
}
}
if (!isValid) {
String message = DotNames.CLIENT_EXCEPTION_MAPPER
+ " is only supported on static methods of REST Client interfaces that take 'jakarta.ws.rs.core.Response' as a single parameter and return 'java.lang.RuntimeException'.";
if (targetMethod != null) {
message += " Offending instance is '" + targetMethod.declaringClass().name().toString() + "#"
+ targetMethod.name() + "'";
}
throw new IllegalStateException(message);
}
int priority = Priorities.USER;
AnnotationValue priorityAnnotationValue = instance.value("priority");
if (priorityAnnotationValue != null) {
priority = priorityAnnotationValue.asInt();
}
ClassInfo restClientInterfaceClassInfo = targetMethod.declaringClass();
String generatedClassName = getGeneratedClassName(targetMethod);
final MethodInfo target = targetMethod;
final int finalPriority = priority;
gizmo.class_(generatedClassName, cc -> {
cc.implements_(ResteasyReactiveResponseExceptionMapper.class);
cc.defaultConstructor();
cc.method("toThrowable", mc -> {
mc.returning(Throwable.class);
ParamVar response = mc.parameter("response", Response.class);View on GitHub (pinned to e1c734241f)
Solutions
- Change the method to return RuntimeException (or a subclass) and accept exactly one jakarta.ws.rs.core.Response parameter
- Make the method static and keep it inside the REST client interface
- Wrap checked exceptions in a RuntimeException subclass before returning
- If you need richer context, capture it in fields/closure-like static state or use ClientRedirectHandler-style alternatives
Example fix
// before
@ClientExceptionMapper
IOException map(Response r) { return new IOException(); } // wrong return type
// after
@ClientExceptionMapper
static RuntimeException map(Response r) {
if (r.getStatus() == 404) return new NotFoundException();
return new InternalServerErrorException();
} Defensive patterns
Strategy: validation
Validate before calling
// Validate signature before annotating:
// static, one jakarta.ws.rs.core.Response param, returns RuntimeException
static boolean validMapperSignature(Method m) {
return Modifier.isStatic(m.getModifiers())
&& m.getParameterCount() == 1
&& m.getParameterTypes()[0] == jakarta.ws.rs.core.Response.class
&& RuntimeException.class.isAssignableFrom(m.getReturnType());
} Try / catch
try {
handler.generateResponseExceptionMapper(classResult, instance);
} catch (IllegalStateException e) {
if (e.getMessage().contains("ClientExceptionMapper")) { /* correct signature per message */ }
throw e;
} Prevention
- Copy the canonical signature: static RuntimeException name(Response r)
- Do not return checked exceptions; wrap them in RuntimeException subclasses
- Do not confuse with @ServerExceptionMapper, which allows more signatures
- Keep the mapper method inside the REST client interface
When it happens
Trigger: @ClientExceptionMapper method that: is not static; takes zero, multiple, or non-Response parameters; returns Throwable/Exception/IOException instead of RuntimeException; is declared outside a REST client interface.
Common situations: Returning a custom checked exception; adding extra convenience parameters (e.g. URI or method) not allowed here; copy-pasting a @ServerExceptionMapper (which allows more signatures) into a client interface.
Related errors
- Only a single instance of '@ClientExceptionMapper' is allowe
- @RegisterClientContextResolver is only supported on static m
- @RegisterClientContextResolver is only supported on static m
- Unsupported parameter type used in @ClientExceptionMapper. S
- @ClientRedirectHandler is only supported on static methods o
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/225d5ace06926c33.
Report an issue: GitHub.