{"record":{"id":"f02139f6ed8b1ffb","repo":"spring-projects/spring-framework","slug":"returning-argument-name-was-not-bound-in-advi","errorCode":null,"errorMessage":"Returning argument name '{}' was not bound in advice arguments","messagePattern":"Returning argument name '(.+?)' was not bound in advice arguments","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java","lineNumber":488,"sourceCode":"\n\t\tint numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterCount();\n\t\tif (this.argumentNames.length != numExpectedArgumentNames) {\n\t\t\tthrow new IllegalStateException(\"Expecting to find \" + numExpectedArgumentNames +\n\t\t\t\t\t\" arguments to bind by name in advice, but actually found \" +\n\t\t\t\t\tthis.argumentNames.length + \" arguments.\");\n\t\t}\n\n\t\t// So we match in number...\n\t\tint argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind;\n\t\tfor (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {\n\t\t\tthis.argumentBindings.put(this.argumentNames[i], i);\n\t\t}\n\n\t\t// Check that returning and throwing were in the argument names list if\n\t\t// specified, and find the discovered argument types.\n\t\tif (this.returningName != null) {\n\t\t\tif (!this.argumentBindings.containsKey(this.returningName)) {\n\t\t\t\tthrow new IllegalStateException(\"Returning argument name '\" + this.returningName +\n\t\t\t\t\t\t\"' was not bound in advice arguments\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tInteger index = this.argumentBindings.get(this.returningName);\n\t\t\t\tthis.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index];\n\t\t\t\tthis.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index];\n\t\t\t}\n\t\t}\n\t\tif (this.throwingName != null) {\n\t\t\tif (!this.argumentBindings.containsKey(this.throwingName)) {\n\t\t\t\tthrow new IllegalStateException(\"Throwing argument name '\" + this.throwingName +\n\t\t\t\t\t\t\"' was not bound in advice arguments\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tInteger index = this.argumentBindings.get(this.throwingName);\n\t\t\t\tthis.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index];\n\t\t\t}\n\t\t}","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java#L470-L506","documentation":"Thrown by bindExplicitArguments (line 486-490) when returningName is set but does not appear as a key in the computed argumentBindings map. The returning name must match one of the advice method's parameter names so Spring knows which slot receives the return value. A mismatch means the configured 'returning' cannot be matched to a parameter.","triggerScenarios":"Configuring returning=\"retVal\" but the advice method parameter is named differently (e.g. 'result'), and parameter names were supplied/ discovered under the wrong identifier. Also when returning is set to a type-restriction string but no parameter matches that binding slot.","commonSituations":"Renaming an advice parameter without updating the returning attribute, or vice versa. Compiling without -parameters so the discovered name differs from the returning name. Mismatched XML returning vs @AfterReturning(returning=).","solutions":["Ensure the returning name exactly matches an advice method parameter name (case-sensitive).","Compile with -parameters so the real parameter name is discovered and matches returning.","If returning was meant as a type restriction, instead declare a parameter of that type and bind by variable name.","Re-check that arg-names (if used) includes the returning name."],"exampleFix":"// before — returning name does not match the parameter\n@AfterReturning(pointcut=\"execution(* svc.*(..))\", returning=\"retVal\")\npublic void after(Object result) { ... }\n\n// after — names agree\n@AfterReturning(pointcut=\"execution(* svc.*(..))\", returning=\"result\")\npublic void after(Object result) { ... }","handlingStrategy":"validation","validationCode":"import java.lang.reflect.Method;\nimport java.util.Arrays;\n\n// Verify the returning name matches an actual parameter name before binding.\nvoid assertReturningNameBound(Method adviceMethod, String returningName, String[] discoveredNames) {\n    String[] names = discoveredNames != null ? discoveredNames\n        : new org.springframework.core.DefaultParameterNameDiscoverer().getParameterNames(adviceMethod);\n    if (names == null || !Arrays.asList(names).contains(returningName)) {\n        throw new IllegalStateException(\"returning '\" + returningName + \"' not found among params \" + Arrays.toString(names));\n    }\n}","typeGuard":"boolean returningNameMatchesParam(Method m, String returningName) {\n    String[] names = new org.springframework.core.DefaultParameterNameDiscoverer().getParameterNames(m);\n    return names != null && java.util.Arrays.asList(names).contains(returningName);\n}","tryCatchPattern":"try {\n    advice.getPointcut();\n} catch (IllegalStateException ex) {\n    if (ex.getMessage().contains(\"Returning argument name\") && ex.getMessage().contains(\"not bound\")) {\n        // align the returning attribute with the actual parameter name\n    } else { throw ex; }\n}","preventionTips":["Keep the returning attribute and the parameter name identical.","Compile with -parameters so names are always available.","Re-check the returning attribute after renaming advice parameters."],"tags":["spring-aop","aspectj","after-returning","argument-binding","naming"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}