spring-projects/spring-framework · error · AmbiguousBindingException
Found potential annotation variable(s) and potential…
Error message
Found {} potential annotation variable(s) and {} potential argument slots What it means
Thrown by AspectJAdviceParameterNameDiscoverer during annotation-based pointcut binding. The pointcut expression uses annotation-binding designators (@annotation, @this, @target, @within, @withincode, @args in binding form) but the advice method has more than one unbound parameter that is a subtype of Annotation. Spring cannot match which annotation parameter corresponds to which binding.
Solutions
- Compile with -parameters so actual parameter names resolve directly without deduction
- Restrict the advice method to a single annotation-typed parameter per binding
- Split the advice into separate advice methods, each handling one annotation binding
Example fix
// before
@Before("@annotation(a)")
public void before(MyAnnotation a, OtherAnnotation b) { ... } // 2 annotation slots, ambiguous
// after
@Before("@annotation(a)")
public void before(MyAnnotation a) { ... } Defensive patterns
Strategy: validation
Validate before calling
// Count annotation-typed unbound parameters and warn if >1 with annotation bindings
long annotationParams = Arrays.stream(adviceMethod.getParameterTypes())
.filter(p -> p != JoinPoint.class && p != ProceedingJoinPoint.class)
.filter(Annotation.class::isAssignableFrom)
.count();
if (annotationParams > 1 && pointcutExpr.contains("@annotation") || pointcutExpr.contains("@args")) {
throw new IllegalStateException("Multiple annotation-typed params with annotation bindings are ambiguous");
} Try / catch
try {
discoverer.getParameterNames(adviceMethod);
} catch (AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException ex) {
// Reduce to one annotation param or split into separate advice methods
} Prevention
- Compile with -parameters
- Use at most one annotation-typed parameter per advice method when relying on @annotation/@this/@target/@within/@args bindings
When it happens
Trigger: An advice method with two or more annotation-typed parameters and a pointcut that uses annotation-binding designators — e.g. @Before("@annotation(a)") void before(MyAnnotation a, OtherAnnotation b) where both are unbound annotation-typed params. Only fires when setRaiseExceptions(true).
Common situations: Multiple annotation bindings in a single pointcut combined with multiple annotation-typed advice parameters, compiled without -parameters.
Related errors
- Found candidate annotation binding variables but only one…
- Binding of returning parameter
- Found candidate reference pointcut 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/b3ca9e7cbf419c6b.
Report an issue: GitHub.
Appendix: source
Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:427
else if (tokens[i].startsWith("@args(") || tokens[i].equals("@args")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
maybeExtractVariableNamesFromArgs(body.text, varNames);
}
}
bindAnnotationsFromVarNames(varNames);
}
/**
* 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
}View on GitHub (pinned to 69bf83ad71)