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

  1. Compile with -parameters so all parameter names are available and ambiguity disappears.
  2. Bind the extra parameters explicitly via returning, throwing, or annotation bindings so exactly one remains at this stage.
  3. 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

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


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/b2332982c70f003d.json. Report an issue: GitHub.