spring-projects/spring-framework · error · AmbiguousBindingException
Still unbound args at reference pointcut binding stage…
Error message
Still {} unbound args at reference pointcut binding stage, with no way to determine between them What it means
Thrown by AspectJAdviceParameterNameDiscoverer at the reference pointcut binding stage (the final deduction stage) when more than one argument remains unbound. A reference pointcut is a named pointcut referenced in the expression (e.g. com.xyz.MyPointcuts.myPc()); the algorithm can only bind one slot from it.
Solutions
- Compile with -parameters so real parameter names resolve without deduction
- Add bindings for remaining parameters via other designators (args, this, target, annotation) earlier in the algorithm
- Reduce the advice method parameters so at most one is unbound by this stage
Example fix
// before — 2 unbound params at reference pointcut stage
@Before("com.example.CommonPointcuts.businessOp(p)")
public void before(Object p, Object extra) { ... }
// after
@Before("com.example.CommonPointcuts.businessOp(p) && args(extra)")
public void before(Object p, Object extra) { ... } Defensive patterns
Strategy: validation
Validate before calling
// This is the last deduction stage; if >1 param is still unbound, deduction cannot succeed.
// Best prevention: compile with -parameters so the discoverer is never consulted.
if (!compiledWithParameters) {
int unbound = adviceMethod.getParameterCount();
// subtract JoinPoint, returning, throwing, all bindings you know about
if (unbound > 1) {
logger.warn("Advice may fail parameter deduction at reference pointcut stage; compile with -parameters");
}
} Try / catch
try {
discoverer.getParameterNames(adviceMethod);
} catch (AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException ex) {
// Add more bindings or compile with -parameters
} Prevention
- Compile with -parameters — this is the definitive fix for all deduction-stage ambiguities
- Add explicit bindings (args/this/target/annotation) for every advice parameter before the reference pointcut stage
When it happens
Trigger: An advice method with two or more remaining unbound parameters whose pointcut references a named pointcut exposing a binding variable — e.g. @Before("com.x.MyAspect.myPc(p)") void before(Object p, Object other). Only fires when setRaiseExceptions(true).
Common situations: Complex composed pointcuts referencing shared pointcut definitions, with advice methods that have extra unmatched parameters, compiled without -parameters.
Related errors
- Found candidate reference pointcut variables but only one…
- Binding of returning parameter
- Found candidate annotation binding variables but only one…
- Found candidate this(), target(), or args() variables but…
- 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/3d08ce60bf228d77.
Report an issue: GitHub.
Appendix: source
Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:533
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate this(), target(), or args() variables but only one unbound argument slot");
}
else if (varNames.size() == 1) {
for (int j = 0; j < this.parameterNameBindings.length; j++) {
if (isUnbound(j)) {
bindParameterName(j, varNames.get(0));
break;
}
}
}
// else varNames.size must be 0 and we have nothing to bind.
}
private void maybeBindReferencePointcutParameter() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments +
" unbound args at reference pointcut 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++) {
String toMatch = tokens[i];
if (toMatch.startsWith("!")) {
toMatch = toMatch.substring(1);
}
int firstParenIndex = toMatch.indexOf('(');
if (firstParenIndex != -1) {
toMatch = toMatch.substring(0, firstParenIndex);
}
else {
if (tokens.length < i + 2) {
// no "(" and nothing following
continue;View on GitHub (pinned to 69bf83ad71)