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

  1. Compile with -parameters so declared names disambiguate bindings.
  2. 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.
  3. 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

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


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