{"record":{"id":"eb95bfafd84b5086","repo":"spring-projects/spring-framework","slug":"binding-of-returning-parameter-is-ambiguous","errorCode":null,"errorMessage":"Binding of returning parameter '{}' is ambiguous: there are {} candidates. Consider compiling with -parameters in order to make declared parameter names available.","messagePattern":"Binding of returning parameter '(.+?)' is ambiguous: there are (.+?) candidates\\. Consider compiling with -parameters in order to make declared parameter names available\\.","errorType":"exception","errorClass":"AmbiguousBindingException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java","lineNumber":370,"sourceCode":"\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) {\n\t\t\t\tthrow new AmbiguousBindingException(\"Binding of returning parameter '\" + this.returningName +\n\t\t\t\t\t\t\"' is ambiguous: there are \" + this.numberOfRemainingUnboundArguments + \" candidates. \" +\n\t\t\t\t\t\t\"Consider compiling with -parameters in order to make declared parameter names available.\");\n\t\t\t}\n\n\t\t\t// We're all set... find the unbound parameter, and bind it.\n\t\t\tfor (int i = 0; i < this.parameterNameBindings.length; i++) {\n\t\t\t\tif (this.parameterNameBindings[i] == null) {\n\t\t\t\t\tbindParameterName(i, this.returningName);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse the string pointcut expression looking for:\n\t * &#64;this, &#64;target, &#64;args, &#64;within, &#64;withincode, &#64;annotation.\n\t * If we find one of these pointcut expressions, try and extract a candidate variable","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java#L352-L388","documentation":"Thrown by AspectJAdviceParameterNameDiscoverer when an @AfterReturning advice with a 'returning' attribute has more than one unbound parameter at the returning-binding stage. Spring cannot determine which parameter should receive the method's return value. This discoverer deduces parameter names from the pointcut expression when real names aren't in the bytecode; with multiple unbound candidates the deduction is ambiguous.","triggerScenarios":"An @AfterReturning(pointcut=\"...\", returning=\"r\") advice method with two or more unbound parameters after JoinPoint/throwing stages — e.g. void after(Object r, Object extra) where both r and extra are unbound. Only fires when setRaiseExceptions(true) is set; otherwise returns null silently.","commonSituations":"Advice compiled without the -parameters javac flag (parameter names stripped from bytecode), advice methods with extra parameters not referenced by the pointcut, or mixing a returning parameter with other untyped parameters.","solutions":["Compile with -parameters (javac -parameters, or maven-compiler-plugin <parameters>true</parameters>) so real parameter names are available and Spring does not need to deduce them","Reduce the advice method to exactly one unbound parameter (plus optional JoinPoint as the first param) for the returning binding","Add an explicit pointcut binding (args/this/target) for each extra parameter so only one remains for returning"],"exampleFix":"// before\n@AfterReturning(pointcut = \"execution(* com.example..*.*(..))\", returning = \"r\")\npublic void after(Object r, Object extra) { ... } // ambiguous: two unbound\n\n// after\n@AfterReturning(pointcut = \"execution(* com.example..*.*(..))\", returning = \"r\")\npublic void after(Object r) { ... }","handlingStrategy":"validation","validationCode":"// Before deploying advice, verify the @AfterReturning method has at most one\n// unbound param beyond JoinPoint when 'returning' is set.\nMethod advice = MyAspect.class.getDeclaredMethod(\"after\", JoinPoint.class, Object.class, Object.class);\nint unbound = advice.getParameterCount();\nClass<?>[] types = advice.getParameterTypes();\nif (types.length > 0 && (types[0] == JoinPoint.class || types[0] == ProceedingJoinPoint.class)) {\n    unbound--; // JoinPoint auto-binds\n}\nif (unbound > 1) {\n    throw new IllegalStateException(\n        \"@AfterReturning advice has \" + unbound + \" unbound params; compile with -parameters or reduce params\");\n}","typeGuard":null,"tryCatchPattern":"AspectJAdviceParameterNameDiscoverer d = new AspectJAdviceParameterNameDiscoverer(expr);\nd.setRaiseExceptions(true);\ntry {\n    String[] names = d.getParameterNames(adviceMethod);\n} catch (AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException ex) {\n    // Deduction failed — fall back to -parameters based discovery or fix the advice signature\n    logger.warn(\"Returning parameter binding ambiguous; compile with -parameters\", ex);\n}","preventionTips":["Always compile with -parameters (javac -parameters / maven-compiler-plugin <parameters>true</parameters>)","Design @AfterReturning advice with exactly one parameter for the returning binding plus an optional JoinPoint"],"tags":["spring-aop","aspectj","after-returning","parameter-binding"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}