{"record":{"id":"58d0fef5564d9c28","repo":"quarkusio/quarkus","slug":"when-serverexceptionmapper-is-used-without-a-va","errorCode":null,"errorMessage":"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()}'","messagePattern":"When '@ServerExceptionMapper' is used without a value, then the annotated method must contain a method parameter that extends 'Throwable'\\. Offending method is '(.+?)' of class '(.+?)'","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/exceptionmappers/ServerExceptionMapperGenerator.java","lineNumber":370,"sourceCode":"        // handle the case where we deduce the type of exception handler by the Throwable defined in method parameters\n        Type deducedHandledExceptionType = null;\n        List<Type> methodParameters = targetMethod.parameterTypes();\n        for (Type methodParameter : methodParameters) {\n            if (methodParameter.kind() == Type.Kind.CLASS) {\n                Class<?> methodParameterClass = getClassByName(methodParameter.name().toString());\n                if (methodParameterClass != null && Throwable.class.isAssignableFrom(methodParameterClass)) {\n                    if (deducedHandledExceptionType != null) {\n                        throw new IllegalArgumentException(\n                                \"Multiple method parameters found that extend 'Throwable'. When using '@ServerExceptionMapper', only one parameter can be of type 'Throwable'. Offending method is '\"\n                                        + targetMethod.name() + \"' of class '\"\n                                        + targetMethod.declaringClass().name().toString() + \"'\");\n                    }\n                    deducedHandledExceptionType = methodParameter;\n                }\n            }\n        }\n        if (deducedHandledExceptionType == null) {\n            throw new IllegalArgumentException(\n                    \"When '@ServerExceptionMapper' is used without a value, then the annotated method must contain a method parameter that extends 'Throwable'. Offending method is '\"\n                            + targetMethod.name() + \"' of class '\" + targetMethod.declaringClass().name().toString() + \"'\");\n        }\n        return new Type[] { deducedHandledExceptionType };\n    }\n\n    private static Set<String> getCommonHierarchyOfExceptions(Type[] handledExceptions) {\n        Set<String> commonHierarchy = new HashSet<>();\n        boolean first = true;\n        for (Type handledException : handledExceptions) {\n            Class<?> handledExceptionClass = getClassByName(handledException.name().toString());\n            while (handledExceptionClass != null && !handledExceptionClass.equals(Throwable.class)) {\n                String handledExceptionClassName = handledExceptionClass.getName();\n                if (first) {\n                    commonHierarchy.add(handledExceptionClassName);\n                } else if (!commonHierarchy.contains(handledExceptionClassName)) {\n                    commonHierarchy.remove(handledExceptionClassName);\n                }","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/exceptionmappers/ServerExceptionMapperGenerator.java#L352-L388","documentation":"When @ServerExceptionMapper is used without a value, RESTEasy Reactive must infer the handled exception from a Throwable-typed method parameter. If no parameter extends Throwable, the handled exception type cannot be determined and an IllegalArgumentException is thrown at build time.","triggerScenarios":"@ServerExceptionMapper (no value) applied to a method whose parameters are all non-Throwable types (e.g. only UriInfo, HttpHeaders, or nothing).","commonSituations":"Forgetting to declare the exception parameter; adding @ServerExceptionMapper to a helper method that takes only request context objects; renaming/refactoring away the exception parameter.","solutions":["Add a parameter that extends Throwable (the exception you want to map)","Or specify the handled type explicitly: @ServerExceptionMapper(NotFoundException.class) and keep a Throwable-compatible signature","Check that the method signature wasn't accidentally changed during refactoring"],"exampleFix":"// before\n@ServerExceptionMapper\npublic Response map(UriInfo uriInfo) {...}\n// after\n@ServerExceptionMapper\npublic Response map(NotFoundException e, UriInfo uriInfo) {...}","handlingStrategy":"validation","validationCode":"// Ensure value-less @ServerExceptionMapper methods declare a Throwable param\nfor (Method m : mapperMethods) {\n  boolean hasThrowable = java.util.Arrays.stream(m.getParameterTypes())\n      .anyMatch(Throwable.class::isAssignableFrom);\n  if (!hasThrowable) throw new IllegalStateException(m + \" needs a Throwable parameter\");\n}","typeGuard":"boolean declaresThrowableParam(Method m) {\n  return java.util.Arrays.stream(m.getParameterTypes())\n    .anyMatch(Throwable.class::isAssignableFrom);\n}","tryCatchPattern":null,"preventionTips":["Always list the mapped exception as the first mapper parameter","Use @ServerExceptionMapper(SpecificException.class) only together with a Throwable-compatible signature","Run an ArchUnit/unit check over mapper methods in CI","Re-check signatures after refactors that touch handler parameters"],"tags":["quarkus","resteasy-reactive","serverexceptionmapper","build-time"],"backgroundTag":"exception-mapper-parameter-validation","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"}