spring-projects/spring-framework · error · AmbiguousBindingException
Found {} potential annotation variable(s) and {} potential a
Error message
Found {} potential annotation variable(s) and {} potential argument slots What it means
Thrown during the annotation-binding stage when the pointcut expression yields candidate annotation variable names AND there is more than one unbound Annotation-typed argument slot in the advice method. Spring cannot map the extracted names to slots unambiguously. Only thrown when raiseExceptions=true; otherwise null is returned.
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 e8729d0438)
Solutions
- Compile with -parameters so declared names disambiguate bindings.
- Ensure there is at most one unbound Annotation-typed parameter when the pointcut contains annotation binding primitives, or match each annotation parameter explicitly in the pointcut.
- Split the advice into multiple advices each binding a single annotation.
Example fix
// before
@Before("@annotation(audit) && @annotation(sec)")
public void check(Audit audit, Security sec) { ... }
// after
@Before("@annotation(audit) && @annotation(sec)")
public void check(Audit audit, Security sec) { ... }
// and compile with -parameters, OR use two separate @Before methods each binding one annotation Defensive patterns
Strategy: validation
Validate before calling
// Ensure at most one unbound Annotation-typed param when pointcut has annotation bindings:
long unboundAnnotationParams = Arrays.stream(method.getParameterTypes())
.filter(p -> Annotation.class.isAssignableFrom(p))
.count();
assert unboundAnnotationParams <= 1; Prevention
- Compile with -parameters.
- Keep annotation-binding advice methods to a single Annotation parameter, or one per @annotation clause.
- Use ajc which embeds parameter names if javac -parameters is not an option.
When it happens
Trigger: A pointcut using binding forms of @annotation/@this/@target/@args/@within/@withincode with more than one Annotation-typed parameter left unbound in the advice method signature.
Common situations: Writing an advice that observes multiple annotations at once without compiled parameter names; combining @args (which can produce multiple binding names) with several Annotation parameters.
Related errors
- Found {} candidate annotation binding variables but only one
- 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/b3ca9e7cbf419c6b.json.
Report an issue: GitHub.