{"record":{"id":"1d8e20731d06354c","repo":"quarkusio/quarkus","slug":"exceptionhandler-methods-in-controlleradvice-mus","errorCode":null,"errorMessage":"@ExceptionHandler methods in @ControllerAdvice must be public instance methods","messagePattern":"@ExceptionHandler methods in @ControllerAdvice must be public instance methods","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/spring-web/core/deployment/src/main/java/io/quarkus/spring/web/deployment/SpringWebProcessor.java","lineNumber":216,"sourceCode":"            BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer,\n            BuildProducer<UnremovableBeanBuildItem> unremovableBeanProducer, IndexView index, ClassOutput classOutput,\n            TypesUtil typesUtil, boolean isResteasyClassic) {\n\n        AnnotationInstance controllerAdviceInstance = getSingleControllerAdviceInstance(index);\n        if (controllerAdviceInstance == null) {\n            return;\n        }\n\n        ClassInfo controllerAdvice = controllerAdviceInstance.target().asClass();\n        List<MethodInfo> methods = controllerAdvice.methods();\n        for (MethodInfo method : methods) {\n            AnnotationInstance exceptionHandlerInstance = method.annotation(EXCEPTION_HANDLER);\n            if (exceptionHandlerInstance == null) {\n                continue;\n            }\n\n            if (!Modifier.isPublic(method.flags()) || Modifier.isStatic(method.flags())) {\n                throw new IllegalStateException(\n                        \"@ExceptionHandler methods in @ControllerAdvice must be public instance methods\");\n            }\n\n            DotName returnTypeDotName = method.returnType().name();\n            if (DISALLOWED_EXCEPTION_CONTROLLER_RETURN_TYPES.contains(returnTypeDotName)) {\n                throw new IllegalStateException(\n                        \"@ExceptionHandler methods in @ControllerAdvice classes can only have void, ResponseEntity or POJO return types\");\n            }\n\n            if (!RESPONSE_ENTITY.equals(returnTypeDotName)) {\n                reflectiveClassProducer.produce(\n                        ReflectiveClassBuildItem.builder(returnTypeDotName.toString()).methods().fields().build());\n            }\n\n            // we need to generate one JAX-RS ExceptionMapper per Exception type\n            Type[] handledExceptionTypes = exceptionHandlerInstance.value().asClassArray();\n            for (Type handledExceptionType : handledExceptionTypes) {\n                reflectiveClassProducer.produce(","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spring-web/core/deployment/src/main/java/io/quarkus/spring/web/deployment/SpringWebProcessor.java#L198-L234","documentation":"Quarkus scans @ControllerAdvice classes for @ExceptionHandler methods and generates ExceptionMapper classes for them via bytecode recording. Only public, non-static instance methods can be wrapped into JAX-RS mappers, so a method that is non-public or static causes a build-time failure.","triggerScenarios":"Declaring an @ExceptionHandler method inside a @ControllerAdvice class that is private/protected/package-private, or declared static.","commonSituations":"Copying Spring code where visibility was relaxed; making a helper handler static because it uses no state; IDE-generated handler methods with default visibility.","solutions":["Make the @ExceptionHandler method public and remove the static modifier.","If it doesn't need Spring request state, move static logic into a helper called from a public instance handler.","Split unrelated handlers into separate @ControllerAdvice classes if visibility rules conflict."],"exampleFix":"// before\n@ExceptionHandler(IllegalArgumentException.class)\nstatic ResponseEntity<String> handle(IllegalArgumentException e) { ... }\n\n// after\n@ExceptionHandler(IllegalArgumentException.class)\npublic ResponseEntity<String> handle(IllegalArgumentException e) { ... }","handlingStrategy":"validation","validationCode":"// audit advice methods before build\n@ControllerAdvice\npublic class Advice {\n  @ExceptionHandler(MyEx.class)\n  public ResponseEntity<?> handle(MyEx e) { ... } // must be public, non-static\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Convention: every @ExceptionHandler method is public and an instance method.","Never mark handler methods static; use injected fields instead."],"tags":["quarkus","spring-web","controlleradvice","build-time"],"backgroundTag":"invalid-method-visibility","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"}