spring-projects/spring-framework · error · AmbiguousBindingException

Found candidate this(), target(), or args() variables but…

Error message

Found {} candidate this(), target(), or args() variables but only one unbound argument slot

What it means

Thrown when exactly one unbound argument slot remains but the pointcut's this()/target()/args() expressions yield multiple candidate variable names. Spring cannot pick which candidate to bind to the single remaining slot.

Solutions

  1. Compile with -parameters to resolve parameter names directly
  2. Reduce the pointcut to a single binding designator matching the single remaining parameter
  3. Add the missing parameter to the advice method so each candidate variable has its own slot

Example fix

// before
@Before("target(t) && args(a)")
public void before(Object t) { ... } // 2 candidates, 1 slot

// after
@Before("target(t)")
public void before(Object t) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// If only one param slot is unbound, ensure the pointcut has at most one of this()/target()/args() binding
long bindingDesignators = Arrays.stream(new String[]{"this(","target(","args("})
    .filter(pointcutExpr::contains).count();
int unbound = /* computed as in error 23 */ 1;
if (unbound == 1 && bindingDesignators > 1) {
    throw new IllegalStateException("Multiple this()/target()/args() bindings but only one unbound slot");
}

Try / catch

try {
    discoverer.getParameterNames(adviceMethod);
} catch (AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException ex) {
    // Reduce binding designators to one, or add params for each
}

Prevention

When it happens

Trigger: A pointcut like target(t) && args(a) with an advice method that has exactly one unbound parameter — e.g. void before(Object t). The algorithm finds two candidate variable names (t and a) but only one slot. Only fires when setRaiseExceptions(true).

Common situations: Multiple binding designators in a pointcut but the advice takes fewer parameters, compiled without -parameters.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/1e929e7797f0e265. Report an issue: GitHub.

Appendix: source

Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:517

				}
			}
			else if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {
				PointcutBody body = getPointcutBody(tokens, i);
				i += body.numTokensConsumed;
				List<String> candidateVarNames = new ArrayList<>();
				maybeExtractVariableNamesFromArgs(body.text, candidateVarNames);
				// we may have found some var names that were bound in previous primitive args binding step,
				// filter them out...
				for (String varName : candidateVarNames) {
					if (!alreadyBound(varName)) {
						varNames.add(varName);
					}
				}
			}
		}

		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");
		}

View on GitHub (pinned to 69bf83ad71)