{"record":{"id":"a785ac615962a174","repo":"spring-projects/spring-framework","slug":"unsupported-afterthrowing-signature-single-throwa","errorCode":null,"errorMessage":"Unsupported afterThrowing signature: single throwable argument or four arguments Method, Object[], target, throwable expected: {}","messagePattern":"Unsupported afterThrowing signature: single throwable argument or four arguments Method, Object\\[\\], target, throwable expected: (.+?)","errorType":"exception","errorClass":"AopConfigException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java","lineNumber":103,"sourceCode":"\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...\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}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java#L85-L121","documentation":"Thrown as AopConfigException by ThrowsAdviceInterceptor's constructor when an afterThrowing method has a parameter count that is neither 1 nor 4. After scanning, throwableParam is still null (line 102), so the signature is unsupported. The ThrowsAdvice contract only allows the one-arg (Throwable) or four-arg (Method, Object[], target, Throwable) forms.","triggerScenarios":"A method named 'afterThrowing' with 0, 2, 3, or 5+ parameters on a ThrowsAdvice implementation. The constructor finds the method by name and then fails the arity check.","commonSituations":"Overloading afterThrowing with extra context parameters; partial implementations; typos that add or drop parameters.","solutions":["Reduce to the supported arity: one Throwable parameter, or four (Method, Object[], Object, Throwable).","Rename unrelated helper methods so they are not called 'afterThrowing'."],"exampleFix":"// before\npublic void afterThrowing(Method m, Object[] args, Object target, Exception ex, Map ctx) { ... } // 5 params\n\n// after\npublic void afterThrowing(Method m, Object[] args, Object target, Exception ex) { ... }","handlingStrategy":"validation","validationCode":"for (Method m : advice.getClass().getMethods()) {\n  if (m.getName().equals(\"afterThrowing\") && m.getParameterCount() != 1 && m.getParameterCount() != 4) {\n    throw new IllegalStateException(\"Unsupported afterThrowing arity: \" + m);\n  }\n}","typeGuard":"static boolean supportedThrowsArity(Method m) {\n  return m.getName().equals(\"afterThrowing\") && (m.getParameterCount() == 1 || m.getParameterCount() == 4);\n}","tryCatchPattern":"try { new ThrowsAdviceInterceptor(advice); }\ncatch (AopConfigException e) {\n  if (e.getMessage().contains(\"Unsupported afterThrowing signature\")) { /* fix arity to 1 or 4 */ }\n  throw e;\n}","preventionTips":["afterThrowing must have either 1 or 4 parameters.","Rename helper methods that are not real handlers."],"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"}