spring-projects/spring-framework · error · AmbiguousBindingException
Still unbound args at this()/target()/args() binding stage…
Error message
Still {} unbound args at this()/target()/args() binding stage, with no way to determine between them What it means
Thrown by AspectJAdviceParameterNameDiscoverer at the this()/target()/args() binding stage when more than one argument remains unbound after all prior stages (JoinPoint, throwing, annotation, returning, primitive). The algorithm can bind at most one remaining slot from this()/target()/args(), so it cannot proceed with two or more unbound arguments.
Solutions
- Compile with -parameters so real parameter names are available
- Add explicit bindings (this/target/args) in the pointcut for each remaining parameter
- Reduce the advice method parameters so at most one is unbound by this stage
Example fix
// before
@Before("execution(* *(..)) && target(t)")
public void before(Object t, Object extra) { ... } // 2 unbound at this stage
// after
@Before("execution(* *(..)) && target(t) && args(extra)")
public void before(Object t, Object extra) { ... } // both bound Defensive patterns
Strategy: validation
Validate before calling
// Count remaining unbound params after accounting for JoinPoint, returning, throwing
int unbound = adviceMethod.getParameterCount();
Class<?>[] types = adviceMethod.getParameterTypes();
if (types.length > 0 && (types[0] == JoinPoint.class || types[0] == ProceedingJoinPoint.class)) unbound--;
if (returningName != null) unbound--;
if (throwingName != null) unbound--;
if (unbound > 1) {
throw new IllegalStateException(unbound + " params remain unbound; add -parameters or more bindings");
} Try / catch
try {
discoverer.getParameterNames(adviceMethod);
} catch (AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException ex) {
// Too many unbound params — add explicit bindings or compile with -parameters
} Prevention
- Compile with -parameters
- Ensure each advice parameter is bound by a pointcut designator (this/target/args/annotation/returning/throwing)
When it happens
Trigger: An advice method with two or more unbound parameters after prior binding stages whose pointcut uses this()/target()/args() designators — e.g. @Before("execution(* *(..)) && target(t)") void before(Object t, Object other). Only fires when setRaiseExceptions(true).
Common situations: Advice methods with more parameters than the pointcut can bind, especially compiled without -parameters.
Related errors
- Found candidate this(), target(), or args() variables but…
- Binding of returning parameter
- Found candidate annotation binding variables but only one…
- Found candidate reference pointcut variables but only one…
- Found candidate variable names but only one candidate…
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/b2332982c70f003d.
Report an issue: GitHub.
Appendix: 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 69bf83ad71)