{"id":"dda5463ab340a8cb","repo":"spring-projects/spring-framework","slug":"at-least-one-handler-method-must-be-found-in-class","errorCode":null,"errorMessage":"At least one handler method must be found in class [{throwsAdvice.getClass()}]","messagePattern":"At least one handler method must be found in class \\[(.+?)\\]","errorType":"exception","errorClass":"AopConfigException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java","lineNumber":119,"sourceCode":"\t\t\t\t}\n\t\t\t\tif (throwableParam == null) {\n\t\t\t\t\tthrow new AopConfigException(\"Unsupported afterThrowing signature: single throwable argument \" +\n\t\t\t\t\t\t\t\"or four arguments Method, Object[], target, throwable expected: \" + method);\n\t\t\t\t}\n\t\t\t\t// An exception handler to register...\n\t\t\t\tMethod existingMethod = this.exceptionHandlerMap.put(throwableParam, method);\n\t\t\t\tif (existingMethod != null) {\n\t\t\t\t\tthrow new AopConfigException(\"Only one afterThrowing method per specific Throwable subclass \" +\n\t\t\t\t\t\t\t\"allowed: \" + method + \" / \" + existingMethod);\n\t\t\t\t}\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Found exception handler method on throws advice: \" + method);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.exceptionHandlerMap.isEmpty()) {\n\t\t\tthrow new AopConfigException(\n\t\t\t\t\t\"At least one handler method must be found in class [\" + throwsAdvice.getClass() + \"]\");\n\t\t}\n\t}\n\n\n\t/**\n\t * Return the number of handler methods in this advice.\n\t */\n\tpublic int getHandlerMethodCount() {\n\t\treturn this.exceptionHandlerMap.size();\n\t}\n\n\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\ttry {\n\t\t\treturn mi.proceed();\n\t\t}","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java#L101-L137","documentation":"After scanning all public methods, if no compliant afterThrowing handler was registered (the map is empty), ThrowsAdviceInterceptor refuses to construct - an exception handler with no handlers is meaningless. AopConfigException is thrown naming the advice class.","triggerScenarios":"Implementing the marker interface org.springframework.aop.ThrowsAdvice without declaring any afterThrowing method; declaring handlers with invalid signatures (so all were rejected before reaching registration, though those throw earlier) or with the wrong method name.","commonSituations":"Empty marker implementation used as a placeholder; typo in method name ('afterthrowing', 'after_Throwing'); methods made non-public so getClass().getMethods() does not see them.","solutions":["Declare at least one public afterThrowing method with a valid 1- or 4-arg signature.","Ensure the method is public (getMethods() returns only public methods).","Check the exact spelling: method must be named 'afterThrowing'."],"exampleFix":"// before\nclass MyAdvice implements ThrowsAdvice { /* no methods */ }\n\n// after\nclass MyAdvice implements ThrowsAdvice {\n  public void afterThrowing(Exception ex) { log(ex); }\n}","handlingStrategy":"validation","validationCode":"long n = java.util.Arrays.stream(advice.getClass().getMethods())\n    .filter(m -> m.getName().equals(\"afterThrowing\")\n        && (m.getParameterCount() == 1 || m.getParameterCount() == 4)\n        && java.lang.reflect.Modifier.isPublic(m.getModifiers()))\n    .count();\nif (n == 0) throw new IllegalStateException(advice.getClass() + \" implements ThrowsAdvice but defines no valid afterThrowing method\");","typeGuard":"public static boolean hasValidAfterThrowing(Class<?> adviceClass) {\n    return java.util.Arrays.stream(adviceClass.getMethods())\n        .anyMatch(m -> m.getName().equals(\"afterThrowing\")\n            && (m.getParameterCount() == 1 || m.getParameterCount() == 4));\n}","tryCatchPattern":null,"preventionTips":["Implement at least one public afterThrowing method on every ThrowsAdvice class.","Verify exact method name spelling and public visibility.","Add a unit test that constructs ThrowsAdviceInterceptor for each advice class at build time."],"tags":["aop","throws-advice","configuration","validation"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}