spring-projects/spring-framework · error · IllegalArgumentException

Found {} candidate annotation binding variables but only one

Error message

Found {} candidate annotation binding variables but only one potential argument binding slot

What it means

Thrown as IllegalArgumentException when the pointcut expression produces multiple candidate annotation-binding variable names but there is exactly one unbound Annotation-typed slot. Spring cannot pick which name maps to the single slot. Only surfaces when raiseExceptions=true.

Source

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

	 * Match the given list of extracted variable names to argument slots.
	 */
	private void bindAnnotationsFromVarNames(List<String> varNames) {
		if (!varNames.isEmpty()) {
			// we have work to do...
			int numAnnotationSlots = countNumberOfUnboundAnnotationArguments();
			if (numAnnotationSlots > 1) {
				throw new AmbiguousBindingException("Found " + varNames.size() +
						" potential annotation variable(s) and " +
						numAnnotationSlots + " potential argument slots");
			}
			else if (numAnnotationSlots == 1) {
				if (varNames.size() == 1) {
					// it's a match
					findAndBind(Annotation.class, varNames.get(0));
				}
				else {
					// multiple candidate vars, but only one slot
					throw new IllegalArgumentException("Found " + varNames.size() +
							" candidate annotation binding variables" +
							" but only one potential argument binding slot");
				}
			}
			else {
				// no slots so presume those candidate vars were actually type names
			}
		}
	}

	/**
	 * If the token starts meets Java identifier conventions, it's in.
	 */
	private @Nullable String maybeExtractVariableName(@Nullable String candidateToken) {
		if (AspectJProxyUtils.isVariableName(candidateToken)) {
			return candidateToken;
		}
		return null;

View on GitHub (pinned to e8729d0438)

Solutions

  1. Make the number of annotation-binding expressions match the number of Annotation-typed parameters.
  2. Remove the extra annotation-binding expression from the pointcut.
  3. Compile with -parameters and bind each annotation explicitly.

Example fix

// before
@Before("@annotation(a) && @annotation(b)")
public void before(MyAnnotation a) { ... }

// after
@Before("@annotation(a) && @annotation(b)")
public void before(MyAnnotation a, OtherAnnotation b) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Count annotation-binding expressions and Annotation params and assert equality:
int bindingClauses = countAnnotationBindingClauses(pointcutExpression);
int annotationParams = countAnnotationParams(adviceMethod);
assert bindingClauses == annotationParams;

Prevention

When it happens

Trigger: A pointcut with two or more annotation-binding expressions (e.g. @annotation(a) @annotation(b)) but only one Annotation-typed parameter in the advice method.

Common situations: Refactoring a pointcut to add a second @annotation binding while forgetting to add the matching parameter; copy-paste errors in pointcut expressions.

Related errors


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