spring-projects/spring-framework · error · AmbiguousBindingException
Still {} unbound args at this()/target()/args() binding stag
Error message
Still {} unbound args at this()/target()/args() binding stage, with no way to determine between them What it means
Thrown at the this()/target()/args() binding stage when more than one parameter remains unbound. With multiple unbound slots and no compiled parameter names, Spring has no way to assign the single candidate variable extracted from this/target/args to the right slot. Only surfaces when raiseExceptions=true.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:483
return;
}
String[] tokens = StringUtils.tokenizeToStringArray(argsSpec, ",");
for (int i = 0; i < tokens.length; i++) {
tokens[i] = tokens[i].strip();
String varName = maybeExtractVariableName(tokens[i]);
if (varName != null) {
varNames.add(varName);
}
}
}
/**
* Parse the string pointcut expression looking for this(), target() and args() expressions.
* If we find one, try and extract a candidate variable name and bind it.
*/
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments +
" unbound args at this()/target()/args() binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<>();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("this") ||
tokens[i].startsWith("this(") ||
tokens[i].equals("target") ||
tokens[i].startsWith("target(")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
String varName = maybeExtractVariableName(body.text);
if (varName != null) {
varNames.add(varName);
}
}
else if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {View on GitHub (pinned to e8729d0438)
Solutions
- Compile with -parameters so all parameter names are available and ambiguity disappears.
- Bind the extra parameters explicitly via returning, throwing, or annotation bindings so exactly one remains at this stage.
- Remove unnecessary parameters from the advice method.
Example fix
// before
@Before("execution(* com.acme.*.*(..)) && target(t)")
public void before(Object t, Object ctx) { ... }
// after
@Before("execution(* com.acme.*.*(..)) && target(t)")
public void before(Object t) { ... }
// or compile with -parameters Defensive patterns
Strategy: validation
Validate before calling
// Ensure at most one unbound param remains before this/target/args stage; simplest: compile -parameters. // Equivalent manual check: reduce advice param count so only one is left unbound after JoinPoint/throwing/annotation/returning stages.
Prevention
- Compile with -parameters.
- Bind every advice parameter via returning/throwing/annotation/args/this/target explicitly.
- Keep advice signatures minimal.
When it happens
Trigger: An advice method with two or more unbound non-primitive parameters reached the this/target/args stage without prior stages (JoinPoint, throwing, annotation, returning) reducing the unbound count to one.
Common situations: Advice methods with several parameters where only some are bound via the pointcut and the rest are ambiguous; missing -parameters compiler flag.
Related errors
- Binding of returning parameter '{}' is ambiguous: there are
- MethodInvocation is not a Spring ProxyMethodInvocation: {}
- 'argumentNames' property of AbstractAspectJAdvice contains a
- Only afterReturning advice can be used to bind a return valu
- Only afterThrowing advice can be used to bind a thrown excep
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/b2332982c70f003d.json.
Report an issue: GitHub.