spring-projects/spring-framework · error · IllegalStateException

Required to bind arguments, but only bound (JoinPointMatch…

Error message

Required to bind {} arguments, but only bound {} (JoinPointMatch {} bound in invocation)

What it means

Thrown by argBinding (line 602-606) at advice invocation time when the number of bound arguments (numBound) does not equal parameterTypes.length. numBound counts the joinpoint/static-part binding plus pointcut parameter bindings plus returning/throwing bindings. A shortfall means a parameter could not be populated — usually because the JoinPointMatch was null (pointcut did not actually match at runtime, so parameterBindings was empty) or because the pointcut bound fewer variables than the method expects.

Solutions

  1. Align the advice method's parameter count with the variables bound by the pointcut expression (args(), this(), target(), @annotation(), returning, throwing).
  2. If the message says 'JoinPointMatch was NOT bound', verify the pointcut actually matches the target method and that the proxy is correctly configured.
  3. Add explicit arg-names / use -parameters so Spring can match each parameter to a binding.
  4. Reduce the method to only the parameters you actually bind.

Example fix

// before — advice declares a param the pointcut does not bind
@After("execution(* svc.*(..)) && args(payload)")
public void after(JoinPoint jp, Object payload, Object extra) { ... } // extra unbound

// after — drop the unbindable parameter
@After("execution(* svc.*(..)) && args(payload)")
public void after(JoinPoint jp, Object payload) { ... }
Defensive patterns

Strategy: validation

Validate before calling

import java.lang.reflect.Method;

// Sanity-check that the pointcut binds exactly as many variables as the advice needs.
// (Heuristic: count binding forms args/this/target/@annotation occurrences vs params minus joinpoint.)
int bindableParamCount(Method adviceMethod) {
    int pc = adviceMethod.getParameterCount();
    Class<?> first = pc > 0 ? adviceMethod.getParameterTypes()[0] : null;
    boolean implicitJp = first == org.aspectj.lang.JoinPoint.class
            || first == org.aspectj.lang.ProceedingJoinPoint.class
            || first == org.aspectj.lang.JoinPoint.StaticPart.class;
    return implicitJp ? pc - 1 : pc;
}
// Ensure your pointcut binds exactly this many variables.

Type guard

boolean pointcutBindsEnoughVariables(String pointcutExpr, int paramsNeedingBinding) {
    // crude count of binding forms; refine as needed
    int forms = 0;
    for (String kw : new String[]{"args(", "this(", "target(", "@annotation(", "@args(", "@this(", "@target("}) {
        int idx = -1;
        while ((idx = pointcutExpr.indexOf(kw, idx + 1)) >= 0) forms++;
    }
    return forms >= paramsNeedingBinding;
}

Try / catch

// This fires at invocation time, not setup. Catch around the proxied call in integration tests:
try {
    proxy.businessMethod();
} catch (IllegalStateException ex) {
    if (ex.getMessage().contains("Required to bind") && ex.getMessage().contains("only bound")) {
        // reconcile pointcut bindings with advice parameters
    } else { throw ex; }
}

Prevention

When it happens

Trigger: An advice method declares more parameters than the pointcut expression binds, and the runtime JoinPointMatch is null (no bindings available). Also when a pointcut uses args(a,b) but the advice declares three parameters, or when the proxy somehow invokes advice without the matching context attached.

Common situations: Mismatch between pointcut binding variables and advice parameters, pointcut expression changed to bind fewer variables without updating the method, or running through a code path that does not attach the JoinPointMatch (e.g. manual invocation). The message distinguishes whether jpMatch was bound.

Related errors


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

Appendix: source

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

			}
			// binding from returning clause
			if (this.returningName != null) {
				Integer index = this.argumentBindings.get(this.returningName);
				Assert.state(index != null, "Index must not be null");
				adviceInvocationArgs[index] = returnValue;
				numBound++;
			}
			// binding from thrown exception
			if (this.throwingName != null) {
				Integer index = this.argumentBindings.get(this.throwingName);
				Assert.state(index != null, "Index must not be null");
				adviceInvocationArgs[index] = ex;
				numBound++;
			}
		}

		if (numBound != this.parameterTypes.length) {
			throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
					" arguments, but only bound " + numBound + " (JoinPointMatch " +
					(jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
		}

		return adviceInvocationArgs;
	}


	/**
	 * Invoke the advice method.
	 * @param jpMatch the JoinPointMatch that matched this execution join point
	 * @param returnValue the return value from the method execution (may be null)
	 * @param ex the exception thrown by the method execution (may be null)
	 * @return the invocation result
	 * @throws Throwable in case of invocation failure
	 */
	protected @Nullable Object invokeAdviceMethod(@Nullable JoinPointMatch jpMatch,
			@Nullable Object returnValue, @Nullable Throwable ex) throws Throwable {

View on GitHub (pinned to 69bf83ad71)