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
- Make the number of annotation-binding expressions match the number of Annotation-typed parameters.
- Remove the extra annotation-binding expression from the pointcut.
- 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
- Pair every @annotation/@args/@this/etc. binding with exactly one Annotation parameter.
- Compile with -parameters.
- Review pointcut strings after refactoring advice signatures.
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
- Found {} potential annotation variable(s) and {} potential a
- Binding of returning parameter '{}' is ambiguous: there are
- Still {} unbound args at this()/target()/args() binding stag
- Found {} candidate this(), target(), or args() variables but
- Still {} unbound args at reference pointcut binding stage, w
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/d031b96b4d4fe1b5.json.
Report an issue: GitHub.