{"record":{"id":"2766530d43837311","repo":"spring-projects/spring-framework","slug":"invalid-afterthrowing-signature-four-arguments-mu","errorCode":null,"errorMessage":"Invalid afterThrowing signature: four arguments must be Method, Object[], target, throwable: {}","messagePattern":"Invalid afterThrowing signature: four arguments must be Method, Object\\[\\], target, throwable: (.+?)","errorType":"exception","errorClass":"AopConfigException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java","lineNumber":97,"sourceCode":"\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...\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}","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java#L79-L115","documentation":"Thrown as AopConfigException by ThrowsAdviceInterceptor's constructor when an afterThrowing method has four parameters but they do not match (Method, Object[], target, ThrowableSubclass). The check at lines 95-96 requires paramTypes[0]==Method.class, paramTypes[1]==Object[].class, paramTypes[2] != Throwable.class (target may be any non-Throwable type, normally Object), and paramTypes[3] a Throwable subclass.","triggerScenarios":"Defining 'afterThrowing(Method, Object, Object, Exception)' (second param Object not Object[]), or 'afterThrowing(Method, Object[], Throwable, Exception)' (target position is Throwable), or any four-arg variant with the wrong types.","commonSituations":"Mistakenly using Object instead of Object[] for the args parameter; reordering arguments; using Throwable as the target parameter type.","solutions":["Match the canonical signature exactly: 'public void afterThrowing(Method m, Object[] args, Object target, SomeThrowable ex)'.","If you do not need the context, drop to the single-Throwable-argument form.","Double-check param order: Method first, Object[] args second, target third, Throwable fourth."],"exampleFix":"// before\npublic void afterThrowing(Method m, Object args, Object target, Exception ex) { ... } // Object not Object[]\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() == 4) {\n    Class<?>[] p = m.getParameterTypes();\n    if (!Method.class.equals(p[0]) || !Object[].class.equals(p[1])\n        || Throwable.class.equals(p[2]) || !Throwable.class.isAssignableFrom(p[3])) {\n      throw new IllegalStateException(\"Bad 4-arg afterThrowing: \" + m);\n    }\n  }\n}","typeGuard":"static boolean validFourArgThrows(Method m) {\n  if (m.getParameterCount() != 4) return false;\n  Class<?>[] p = m.getParameterTypes();\n  return Method.class.equals(p[0]) && Object[].class.equals(p[1])\n      && !Throwable.class.equals(p[2]) && Throwable.class.isAssignableFrom(p[3]);\n}","tryCatchPattern":"try { new ThrowsAdviceInterceptor(advice); }\ncatch (AopConfigException e) {\n  if (e.getMessage().contains(\"four arguments must be\")) { /* fix to (Method, Object[], Object, Throwable) */ }\n  throw e;\n}","preventionTips":["Use exactly (Method, Object[], Object, ThrowableSubclass).","Remember the target slot cannot be exactly Throwable."],"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"}