quarkusio/quarkus · error · IllegalArgumentException
Multiple method parameters found that extend 'Throwable'. Wh
Error message
Multiple method parameters found that extend 'Throwable'. When using '@ServerExceptionMapper', only one parameter can be of type 'Throwable'. Offending method is '${targetMethod.name()}' of class '${targetMethod.declaringClass().name().toString()}' What it means
getHandledExceptionTypes deduces which exception type a @ServerExceptionMapper handles when the annotation has no value: it scans method parameters for Throwable subtypes. If two or more parameters are Throwable subtypes, the handled type is ambiguous, so an IllegalArgumentException is thrown at build time.
Source
Thrown at independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/exceptionmappers/ServerExceptionMapperGenerator.java:360
private static Type[] getHandledExceptionTypes(MethodInfo targetMethod) {
AnnotationValue annotationValue = targetMethod.annotation(SERVER_EXCEPTION_MAPPER).value();
// handle the case where 'value' is set
if (annotationValue != null) {
Type[] valueArray = annotationValue.asClassArray();
if ((valueArray != null) && (valueArray.length > 0)) {
return valueArray;
}
}
// handle the case where we deduce the type of exception handler by the Throwable defined in method parameters
Type deducedHandledExceptionType = null;
List<Type> methodParameters = targetMethod.parameterTypes();
for (Type methodParameter : methodParameters) {
if (methodParameter.kind() == Type.Kind.CLASS) {
Class<?> methodParameterClass = getClassByName(methodParameter.name().toString());
if (methodParameterClass != null && Throwable.class.isAssignableFrom(methodParameterClass)) {
if (deducedHandledExceptionType != null) {
throw new IllegalArgumentException(
"Multiple method parameters found that extend 'Throwable'. When using '@ServerExceptionMapper', only one parameter can be of type 'Throwable'. Offending method is '"
+ targetMethod.name() + "' of class '"
+ targetMethod.declaringClass().name().toString() + "'");
}
deducedHandledExceptionType = methodParameter;
}
}
}
if (deducedHandledExceptionType == null) {
throw new IllegalArgumentException(
"When '@ServerExceptionMapper' is used without a value, then the annotated method must contain a method parameter that extends 'Throwable'. Offending method is '"
+ targetMethod.name() + "' of class '" + targetMethod.declaringClass().name().toString() + "'");
}
return new Type[] { deducedHandledExceptionType };
}
private static Set<String> getCommonHierarchyOfExceptions(Type[] handledExceptions) {
Set<String> commonHierarchy = new HashSet<>();View on GitHub (pinned to e1c734241f)
Solutions
- Keep exactly one Throwable parameter and remove the other
- Handle additional exception types with separate @ServerExceptionMapper methods, each with one Throwable parameter
- Specify the handled exception type explicitly via @ServerExceptionMapper(SomeException.class) if extra Throwable-typed data must be passed
Example fix
// before
@ServerExceptionMapper
public Response map(IllegalArgumentException e, IllegalStateException cause) {...}
// after
@ServerExceptionMapper
public Response map(IllegalArgumentException e) {...} Defensive patterns
Strategy: validation
Validate before calling
// Ensure each value-less @ServerExceptionMapper method has at most one Throwable param
for (Method m : mapperMethods) {
long count = java.util.Arrays.stream(m.getParameterTypes())
.filter(Throwable.class::isAssignableFrom).count();
if (count > 1) throw new IllegalStateException(m + " has " + count + " Throwable parameters");
} Type guard
boolean hasSingleThrowableParam(Method m) {
return java.util.Arrays.stream(m.getParameterTypes())
.filter(Throwable.class::isAssignableFrom).count() == 1;
} Prevention
- One exception parameter per mapper method
- Split multi-exception handling into separate mapper methods
- Use instanceof checks inside a single Throwable-parameter mapper instead of multiple exception params
- Review mapper signatures when merging handlers
When it happens
Trigger: A @ServerExceptionMapper-annotated method without an explicit annotation value declares two parameters that both extend Throwable (e.g. (RuntimeException, MyError)).
Common situations: Adding an extra context-like exception parameter by mistake; combining the mapped exception with a fallback Throwable parameter; copy-paste from a multi-catch handler.
Related errors
- ReturnType: '${returnType}' is not supported
- When '@ServerExceptionMapper' is used without a value, then
- Parameter '${parameterName}' of method '${targetMethod.name(
- Parameter '${parameterName}' of method '${targetMethod.name(
- The combination of '@${annotationName}' and '@ServerRequestF
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/dcba5c398bdf0391.
Report an issue: GitHub.