spring-projects/spring-framework · error · AmbiguousBindingException
Found {} candidate variable names but only one candidate bin
Error message
Found {} candidate variable names but only one candidate binding slot when matching primitive args What it means
Thrown when the args() clause of the pointcut yields multiple candidate variable names for primitive binding but only one unbound primitive slot exists. Spring cannot decide which name maps to the single primitive parameter. Only surfaces when raiseExceptions=true.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:659
private void maybeBindPrimitiveArgsFromPointcutExpression() {
int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments();
if (numUnboundPrimitives > 1) {
throw new AmbiguousBindingException("Found " + numUnboundPrimitives +
" unbound primitive arguments with no way to distinguish between them.");
}
if (numUnboundPrimitives == 1) {
// Look for arg variable and bind it if we find exactly one...
List<String> varNames = new ArrayList<>();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
maybeExtractVariableNamesFromArgs(body.text, varNames);
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate variable names but only one candidate binding slot when matching primitive args");
}
else if (varNames.size() == 1) {
// 1 primitive arg, and one candidate...
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
bindParameterName(i, varNames.get(0));
break;
}
}
}
}
}
/*
* Return true if the parameter name binding for the given parameter
* index has not yet been assigned.
*/View on GitHub (pinned to e8729d0438)
Solutions
- Match the number of args() binding variables to the number of primitive parameters in the advice.
- Use wildcards (*) in args() for parameters you do not want to bind.
- Compile with -parameters for full disambiguation.
Example fix
// before
@Before("execution(* com.acme.*.*(int,int)) && args(a, b)")
public void before(int a) { ... }
// after
@Before("execution(* com.acme.*.*(int,int)) && args(a, *)")
public void before(int a) { ... } Defensive patterns
Strategy: validation
Validate before calling
// Match args() primitive var count to unbound primitive param count: int argsVars = countArgsBindingVars(pointcutExpression); int unboundPrimitives = countUnboundPrimitiveParams(adviceMethod); assert argsVars == unboundPrimitives;
Prevention
- Use wildcards (*) in args() for primitives you do not bind.
- Compile with -parameters.
- Keep args() variable list and advice primitive params in sync.
When it happens
Trigger: A pointcut with args(a, b) where both a and b are candidate names but only one primitive parameter is unbound.
Common situations: Pointcut args() lists more variables than the advice method has primitive parameters; mismatched refactor.
Related errors
- Found {} unbound primitive arguments with no way to distingu
- Binding of returning parameter '{}' is ambiguous: there are
- Found {} potential annotation variable(s) and {} potential a
- Found {} candidate annotation binding variables but only one
- Still {} unbound args at this()/target()/args() binding stag
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/f88a1e975067818e.json.
Report an issue: GitHub.