{"id":"065fe04c3952a0d0","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: {method}","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/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java#L79-L115","documentation":"The 4-argument afterThrowing variant must match exactly (Method, Object[], Object, ThrowableSubclass). The validator checks param[0]==Method, param[1]==Object[], param[2] is NOT Throwable (it is the target), and param[3] is a Throwable subclass. Any deviation throws AopConfigException naming the offending method.","triggerScenarios":"Defining afterThrowing(Method, Object[], Throwable, Object) (swapped last two); afterThrowing(Method, Object[], Object, Object) (non-throwable 4th); using String[] instead of Object[]; target param typed as Throwable.","commonSituations":"Misremembering the parameter order; using a concrete non-throwable type for the 4th slot; IDE auto-importing the wrong Method class.","solutions":["Use the exact signature: afterThrowing(Method m, Object[] args, Object target, ThrowableSubclass ex).","Ensure the 4th param is a Throwable subclass and the 3rd is the plain target object.","If you only need the exception, drop to the single-arg form afterThrowing(Throwable)."],"exampleFix":"// before\npublic void afterThrowing(Method m, Object[] args, Object target, Object o) {}\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 signature: \" + m);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the exact signature (Method, Object[], Object, ThrowableSubclass).","Keep the 3rd param a plain target type, the 4th a Throwable subclass.","If unsure, prefer the single-arg afterThrowing(Throwable) form."],"tags":["aop","throws-advice","signature","configuration"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}