spring-projects/spring-framework · error · IllegalStateException

Expecting to find arguments to bind by name in advice, but…

Error message

Expecting to find {} arguments to bind by name in advice, but actually found {} arguments.

What it means

Thrown by bindExplicitArguments (line 471-476) when the length of the resolved argumentNames array does not equal the advice method's parameter count. Spring requires an exact 1:1 correspondence because each parameter is mapped by index to a pointcut binding. A mismatch indicates the supplied arg-names or discovered names describe a different signature than the method.

Solutions

  1. Make the arg-names list length equal the method parameter count (Spring auto-inserts the joinpoint slot only when argNames.length == paramCount - 1, see line 273).
  2. After changing an advice method signature, update arg-names to match.
  3. If using -parameters, remove arg-names entirely and let Spring discover them.
  4. Verify the pointcut binds exactly the variables named in arg-names.

Example fix

// before — arg-names count != method params (method has 2 params, names has 1)
<aop:after-returning method="after"
    pointcut="execution(* *(..)) && args(payload)"
    arg-names="payload" returning="ret"/>
public void after(JoinPoint jp, Object ret) {} // jp is implicit; needs ret too

// after — include all non-implicit names, or drop arg-names and compile with -parameters
<aop:after-returning method="after"
    pointcut="execution(* *(..)) && args(payload)"
    returning="ret"/>
public void after(JoinPoint jp, Object payload, Object ret) {}
Defensive patterns

Strategy: validation

Validate before calling

import java.lang.reflect.Method;

// Ensure arg-names count matches the method parameter count (accounting for the implicit joinpoint slot).
void assertArgNamesCount(Method adviceMethod, String[] argNames) {
    int pc = adviceMethod.getParameterCount();
    Class<?> first = pc > 0 ? adviceMethod.getParameterTypes()[0] : null;
    boolean hasImplicitJp = first == org.aspectj.lang.JoinPoint.class
            || first == org.aspectj.lang.ProceedingJoinPoint.class
            || first == org.aspectj.lang.JoinPoint.StaticPart.class;
    int expected = hasImplicitJp ? pc - 1 : pc;
    if (argNames.length != expected && argNames.length != pc) {
        throw new IllegalStateException("arg-names count " + argNames.length + " != params " + expected);
    }
}

Type guard

boolean argNamesCountConsistent(Method m, String[] argNames) {
    int pc = m.getParameterCount();
    Class<?> first = pc > 0 ? m.getParameterTypes()[0] : null;
    boolean implicitJp = first == org.aspectj.lang.JoinPoint.class
            || first == org.aspectj.lang.ProceedingJoinPoint.class
            || first == org.aspectj.lang.JoinPoint.StaticPart.class;
    int expected = implicitJp ? pc - 1 : pc;
    return argNames.length == expected || argNames.length == pc;
}

Try / catch

try {
    advice.getPointcut();
} catch (IllegalStateException ex) {
    if (ex.getMessage().contains("Expecting to find")) {
        // reconcile arg-names length with the advice method signature
    } else { throw ex; }
}

Prevention

When it happens

Trigger: Supplying arg-names with more or fewer entries than the method parameters (excluding the auto-inserted joinpoint slot), or parameter-name discovery returning a partial list. For example, a method after(JoinPoint jp, Object r) with arg-names="r" (missing the joinpoint slot) when the auto-insert logic at line 273 does not apply because counts do not line up to the +1 expectation.

Common situations: Mistyping the arg-names count in XML, refactoring the advice method signature (adding/removing a parameter) without updating arg-names, or a mismatch between the @Aspect method and the pointcut's bound variables. Also when debug-discovered names partially succeed.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/8b86c2d5f88ea47e. Report an issue: GitHub.

Appendix: source

Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java:473

		DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
		AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer =
				new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression());
		adviceParameterNameDiscoverer.setReturningName(this.returningName);
		adviceParameterNameDiscoverer.setThrowingName(this.throwingName);
		// Last in chain, so if we're called and we fail, that's bad...
		adviceParameterNameDiscoverer.setRaiseExceptions(true);
		discoverer.addDiscoverer(adviceParameterNameDiscoverer);
		return discoverer;
	}

	@SuppressWarnings("NullAway") // Dataflow analysis limitation
	private void bindExplicitArguments(int numArgumentsLeftToBind) {
		Assert.state(this.argumentNames != null, "No argument names available");
		this.argumentBindings = new HashMap<>();

		int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterCount();
		if (this.argumentNames.length != numExpectedArgumentNames) {
			throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames +
					" arguments to bind by name in advice, but actually found " +
					this.argumentNames.length + " arguments.");
		}

		// So we match in number...
		int argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind;
		for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {
			this.argumentBindings.put(this.argumentNames[i], i);
		}

		// Check that returning and throwing were in the argument names list if
		// specified, and find the discovered argument types.
		if (this.returningName != null) {
			if (!this.argumentBindings.containsKey(this.returningName)) {
				throw new IllegalStateException("Returning argument name '" + this.returningName +
						"' was not bound in advice arguments");
			}
			else {

View on GitHub (pinned to 69bf83ad71)