{"record":{"id":"80b32b2cc638b7ee","repo":"spring-projects/spring-framework","slug":"invalid-afterthrowing-signature-single-argument-m","errorCode":null,"errorMessage":"Invalid afterThrowing signature: single argument must be a Throwable subclass","messagePattern":"Invalid afterThrowing signature: single argument must be a Throwable subclass","errorType":"exception","errorClass":"AopConfigException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java","lineNumber":88,"sourceCode":"\n\t/**\n\t * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.\n\t * @param throwsAdvice the advice object that defines the exception handler methods\n\t * (usually a {@link org.springframework.aop.ThrowsAdvice} implementation)\n\t */\n\tpublic ThrowsAdviceInterceptor(Object throwsAdvice) {\n\t\tAssert.notNull(throwsAdvice, \"Advice must not be null\");\n\t\tthis.throwsAdvice = throwsAdvice;\n\n\t\tMethod[] methods = throwsAdvice.getClass().getMethods();\n\t\tfor (Method method : methods) {\n\t\t\tif (method.getName().equals(AFTER_THROWING)) {\n\t\t\t\tClass<?> throwableParam = null;\n\t\t\t\tif (method.getParameterCount() == 1) {\n\t\t\t\t\t// just a Throwable parameter\n\t\t\t\t\tthrowableParam = method.getParameterTypes()[0];\n\t\t\t\t\tif (!Throwable.class.isAssignableFrom(throwableParam)) {\n\t\t\t\t\t\tthrow new AopConfigException(\"Invalid afterThrowing signature: \" +\n\t\t\t\t\t\t\t\t\"single argument must be a Throwable subclass\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (method.getParameterCount() == 4) {\n\t\t\t\t\t// Method, Object[], target, throwable\n\t\t\t\t\tClass<?>[] paramTypes = method.getParameterTypes();\n\t\t\t\t\tif (!Method.class.equals(paramTypes[0]) || !Object[].class.equals(paramTypes[1]) ||\n\t\t\t\t\t\t\tThrowable.class.equals(paramTypes[2]) || !Throwable.class.isAssignableFrom(paramTypes[3])) {\n\t\t\t\t\t\tthrow new AopConfigException(\"Invalid afterThrowing signature: \" +\n\t\t\t\t\t\t\t\t\"four arguments must be Method, Object[], target, throwable: \" + method);\n\t\t\t\t\t}\n\t\t\t\t\tthrowableParam = paramTypes[3];\n\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...","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java#L70-L106","documentation":"Thrown as AopConfigException by ThrowsAdviceInterceptor's constructor when an afterThrowing method has exactly one parameter but that parameter is not assignable to Throwable. Per the ThrowsAdvice contract, the single-arg form must be 'void afterThrowing(SomeThrowable ex)'.","triggerScenarios":"A ThrowsAdvice implementation defines 'public void afterThrowing(String msg)' or any non-Throwable single argument; the constructor at lines 84-90 rejects it eagerly while scanning the advice class.","commonSituations":"Typos in afterThrowing signatures; copy-paste from a different advice style; believing the argument can be a context object instead of an exception.","solutions":["Change the single argument to a Throwable subclass: 'public void afterThrowing(MyException ex)'.","If you need method/args/target context, use the four-argument form: afterThrowing(Method, Object[], Object, Throwable)."],"exampleFix":"// before\npublic void afterThrowing(String reason) { ... } // non-Throwable\n\n// after\npublic void afterThrowing(IllegalStateException ex) { ... }","handlingStrategy":"validation","validationCode":"for (Method m : advice.getClass().getMethods()) {\n  if (m.getName().equals(\"afterThrowing\") && m.getParameterCount() == 1\n      && !Throwable.class.isAssignableFrom(m.getParameterTypes()[0])) {\n    throw new IllegalStateException(\"Bad afterThrowing: \" + m);\n  }\n}\nnew ThrowsAdviceInterceptor(advice);","typeGuard":"static boolean validSingleArgThrows(Method m) {\n  return m.getParameterCount() == 1 && Throwable.class.isAssignableFrom(m.getParameterTypes()[0]);\n}","tryCatchPattern":"try { new ThrowsAdviceInterceptor(advice); }\ncatch (AopConfigException e) {\n  if (e.getMessage().contains(\"single argument must be a Throwable\")) { /* fix signature */ }\n  throw e;\n}","preventionTips":["Single-arg afterThrowing must take a Throwable subclass.","Prefer compiler-checked exceptions: declare a specific exception type as the parameter."],"tags":["spring-aop","throws-advice","configuration","validation"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}