{"record":{"id":"c4fdf6a837e94465","repo":"quarkusio/quarkus","slug":"only-a-single-instance-of-clientexceptionmapper","errorCode":null,"errorMessage":"Only a single instance of '@ClientExceptionMapper' is allowed per REST Client interface. Offending class is '${class}'","messagePattern":"Only a single instance of '@ClientExceptionMapper' is allowed per REST Client interface\\. Offending class is '(.+?)'","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java","lineNumber":878,"sourceCode":"            BuildProducer<ExecutionModelAnnotationsAllowedBuildItem> executionModelAnnotationsAllowedProducer) {\n\n        executionModelAnnotationsAllowedProducer.produce(new ExecutionModelAnnotationsAllowedBuildItem(\n                new Predicate<>() {\n                    @Override\n                    public boolean test(MethodInfo methodInfo) {\n                        return methodInfo.hasDeclaredAnnotation(CLIENT_EXCEPTION_MAPPER);\n                    }\n                }));\n\n        var result = new HashMap<String, GeneratedClassResult>();\n        ClientExceptionMapperHandler clientExceptionMapperHandler = new ClientExceptionMapperHandler(gizmo);\n        for (AnnotationInstance instance : index.getAnnotations(CLIENT_EXCEPTION_MAPPER)) {\n            GeneratedClassResult classResult = clientExceptionMapperHandler.generateResponseExceptionMapper(instance);\n            if (classResult == null) {\n                continue;\n            }\n            if (result.containsKey(classResult.interfaceName)) {\n                throw new IllegalStateException(\"Only a single instance of '\" + CLIENT_EXCEPTION_MAPPER\n                        + \"' is allowed per REST Client interface. Offending class is '\" + classResult.interfaceName + \"'\");\n            }\n            result.put(classResult.interfaceName, classResult);\n            reflectiveClassesProducer.produce(ReflectiveClassBuildItem.builder(classResult.generatedClassName)\n                    .reason(getClass().getName())\n                    .build());\n        }\n        return result;\n    }\n\n    private Map<String, GeneratedClassResult> populateClientRedirectHandlerFromAnnotations(\n            Gizmo gizmo,\n            BuildProducer<ReflectiveClassBuildItem> reflectiveClasses, IndexView index) {\n\n        var result = new HashMap<String, GeneratedClassResult>();\n        ClientRedirectHandler clientHandler = new ClientRedirectHandler(gizmo);\n        for (AnnotationInstance instance : index.getAnnotations(CLIENT_REDIRECT_HANDLER)) {\n            GeneratedClassResult classResult = clientHandler.generateResponseExceptionMapper(instance);","sourceCodeStart":860,"sourceCodeEnd":896,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java#L860-L896","documentation":"A REST Client interface may declare at most one method annotated with `@ClientExceptionMapper` (including inherited ones). Quarkus generates one exception-mapper class per interface during build and throws IllegalStateException when a second instance targets the same interface.","triggerScenarios":"Two `@ClientExceptionMapper` static methods defined on the same client interface (possibly via interface inheritance or a shared parent interface).","commonSituations":"Adding a new mapper while an inherited default one already exists; multiple interfaces extending a common interface each defining their own mapper and then both applied to one client.","solutions":["Keep only one `@ClientExceptionMapper` method per interface and merge the logic","Move shared mapping logic into a helper method called by the single mapper","Check parent interfaces for inherited mappers and remove duplicates"],"exampleFix":"// before\n@ClientExceptionMapper Response map1(Response r) {...}\n@ClientExceptionMapper Response map2(Response r) {...}\n// after\n@ClientExceptionMapper Response map(Response r) {\n    if (r.getStatus()==404) return notFound();\n    return map2Logic(r);\n}","handlingStrategy":"validation","validationCode":"// Verify at build/test time only one mapper exists per interface\nlong count = Arrays.stream(MyClient.class.getMethods())\n    .filter(m -> m.isAnnotationPresent(ClientExceptionMapper.class))\n    .count();\nif (count > 1) throw new IllegalStateException(\"Duplicate @ClientExceptionMapper on \" + MyClient.class);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Grep for @ClientExceptionMapper across interface hierarchies before adding a new one","Keep mapper logic in one method per interface","Watch for mappers inherited from shared parent interfaces"],"tags":["rest-client","exception-mapper","annotation-processing","build-time"],"backgroundTag":"duplicate-annotation-declaration","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"}