{"record":{"id":"a6cc4a4354a15f26","repo":"spring-projects/spring-framework","slug":"binding-of-throwing-parameter-could-not-be-co","errorCode":null,"errorMessage":"Binding of throwing parameter '{}' could not be completed as no available arguments are a subtype of Throwable","messagePattern":"Binding of throwing parameter '(.+?)' could not be completed as no available arguments are a subtype of Throwable","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java","lineNumber":351,"sourceCode":"\n\t\t// So there is binding work to do...\n\t\tint throwableIndex = -1;\n\t\tfor (int i = 0; i < this.argumentTypes.length; i++) {\n\t\t\tif (isUnbound(i) && isSubtypeOf(Throwable.class, i)) {\n\t\t\t\tif (throwableIndex == -1) {\n\t\t\t\t\tthrowableIndex = i;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Second candidate we've found - ambiguous binding\n\t\t\t\t\tthrow new AmbiguousBindingException(\"Binding of throwing parameter '\" +\n\t\t\t\t\t\t\tthis.throwingName + \"' is ambiguous: could be bound to argument \" +\n\t\t\t\t\t\t\tthrowableIndex + \" or \" + i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (throwableIndex == -1) {\n\t\t\tthrow new IllegalStateException(\"Binding of throwing parameter '\" + this.throwingName +\n\t\t\t\t\t\"' could not be completed as no available arguments are a subtype of Throwable\");\n\t\t}\n\t\telse {\n\t\t\tbindParameterName(throwableIndex, this.throwingName);\n\t\t}\n\t}\n\n\t/**\n\t * If a returning variable was specified and there is only one choice remaining, bind it.\n\t */\n\tprivate void maybeBindReturningVariable() {\n\t\tif (this.numberOfRemainingUnboundArguments == 0) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Algorithm assumes that there must be at least one unbound parameter on entry to this method\");\n\t\t}\n\n\t\tif (this.returningName != null) {\n\t\t\tif (this.numberOfRemainingUnboundArguments > 1) {","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java#L333-L369","documentation":"Thrown by maybeBindThrowingVariable (line 350-353) when a throwingName is set but NONE of the unbound advice parameters is a subtype of Throwable. After-throwing advice binds the thrown exception to a parameter; if no parameter can hold a Throwable, binding is impossible. Raised as IllegalStateException (suppressed unless raiseExceptions, but AbstractAspectJAdvice sets raiseExceptions=true at line 461).","triggerScenarios":"An after-throwing advice declares throwing=\"ex\" but its parameters are all non-Throwable (e.g. after(String msg, Integer code)). Or the only Throwable parameter was already bound to something else (e.g. JoinPoint consumed the slot).","commonSituations":"Declaring an after-throwing advice without a Throwable-typed parameter, or binding the wrong parameter type. Common when converting an after-returning advice to after-throwing and forgetting to change the parameter type, or when the throwing attribute is set but the method signature lacks an exception parameter.","solutions":["Declare at least one advice parameter whose type is a Throwable subtype (e.g. Throwable, RuntimeException, or the specific exception).","Ensure the throwing name matches that parameter (or compile with -parameters).","If the method needs non-exception context too, add the Throwable parameter alongside it."],"exampleFix":"// before — no Throwable parameter to bind the thrown exception\n@AfterThrowing(pointcut=\"execution(* svc.*(..))\", throwing=\"ex\")\npublic void onError(String message) { ... }\n\n// after — add a Throwable-typed parameter\n@AfterThrowing(pointcut=\"execution(* svc.*(..))\", throwing=\"ex\")\npublic void onError(Throwable ex) { ... }","handlingStrategy":"validation","validationCode":"import java.lang.reflect.Method;\n\n// Ensure at least one Throwable parameter exists before setting throwing.\nvoid assertHasThrowableParam(Method adviceMethod) {\n    for (Class<?> t : adviceMethod.getParameterTypes()) {\n        if (Throwable.class.isAssignableFrom(t)) return;\n    }\n    throw new IllegalStateException(\"after-throwing advice needs a Throwable-typed parameter\");\n}","typeGuard":"boolean hasThrowableParam(Method m) {\n    for (Class<?> t : m.getParameterTypes()) {\n        if (Throwable.class.isAssignableFrom(t)) return true;\n    }\n    return false;\n}","tryCatchPattern":"try {\n    advice.getPointcut();\n} catch (IllegalStateException ex) {\n    if (ex.getMessage().contains(\"no available arguments are a subtype of Throwable\")) {\n        // add a Throwable-typed parameter to the advice method\n    } else { throw ex; }\n}","preventionTips":["Always declare a Throwable (or subtype) parameter on after-throwing advice.","When converting after-returning to after-throwing, change the parameter type.","Validate the advice signature against the throwing attribute at startup."],"tags":["spring-aop","aspectj","after-throwing","argument-binding","parameter-discovery"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}