spring-projects/spring-framework · error · IllegalStateException

Not enough arguments in method to satisfy binding of…

Error message

Not enough arguments in method to satisfy binding of returning and throwing variables

What it means

Thrown by AspectJAdviceParameterNameDiscoverer.getParameterNames (line 233-236) when the advice method's parameter count is less than the minimum required to hold both the returning and throwing bindings. minimumNumberUnboundArgs counts 1 per non-null returningName and 1 per non-null throwingName; if the method has fewer parameters than that, it cannot possibly accept both bindings.

Solutions

  1. An advice method is either afterReturning or afterThrowing — set only the corresponding binding (returning XOR throwing), not both.
  2. Ensure the method declares a parameter for each binding (one for returning and/or one for throwing).
  3. Remove the extraneous returning/throwing attribute that does not apply to this advice type.

Example fix

// before — both returning and throwing set, method has 1 param
<aop:after-returning method="after"
    pointcut="execution(* *(..))" returning="r" throwing="ex"/>
public void after(Object r) {} // needs 2 params but has 1

// after — after-returning binds only returning
<aop:after-returning method="after"
    pointcut="execution(* *(..))" returning="r"/>
public void after(Object r) {}
Defensive patterns

Strategy: validation

Validate before calling

import java.lang.reflect.Method;

// Before configuring, ensure the method has enough params for the bindings you intend.
void assertEnoughParamsForBindings(Method adviceMethod, boolean returning, boolean throwing) {
    int needed = (returning ? 1 : 0) + (throwing ? 1 : 0);
    if (adviceMethod.getParameterCount() < needed) {
        throw new IllegalStateException("method has " + adviceMethod.getParameterCount()
            + " params but needs >= " + needed + " for returning/throwing");
    }
}

Type guard

boolean hasEnoughParamsForBindings(Method m, boolean returning, boolean throwing) {
    int needed = (returning ? 1 : 0) + (throwing ? 1 : 0);
    return m.getParameterCount() >= needed;
}

Try / catch

try {
    advice.getPointcut();
} catch (IllegalStateException ex) {
    if (ex.getMessage().contains("returning and throwing variables")) {
        // set only returning XOR throwing, and add the matching parameter
    } else { throw ex; }
}

Prevention

When it happens

Trigger: Configuring an advice method with fewer parameters than the sum of returning + throwing bindings — e.g. a method taking a single Throwable parameter but both returning="r" and throwing="ex" are set. Since an advice can be afterReturning XOR afterThrowing, setting both is itself suspicious, but the discoverer fails when the count is insufficient.

Common situations: Accidentally setting both returning and throwing on the same advice, or an advice method whose signature was trimmed (params removed) without updating the returning/throwing configuration. Common in generic advice-registration code that copies both attributes.

Related errors


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

Appendix: source

Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:234

	 * for this class for details on the algorithm used.
	 * @param method the target {@link Method}
	 * @return the parameter names
	 */
	@Override
	public @Nullable String @Nullable [] getParameterNames(Method method) {
		this.argumentTypes = method.getParameterTypes();
		this.numberOfRemainingUnboundArguments = this.argumentTypes.length;
		this.parameterNameBindings = new String[this.numberOfRemainingUnboundArguments];

		int minimumNumberUnboundArgs = 0;
		if (this.returningName != null) {
			minimumNumberUnboundArgs++;
		}
		if (this.throwingName != null) {
			minimumNumberUnboundArgs++;
		}
		if (this.numberOfRemainingUnboundArguments < minimumNumberUnboundArgs) {
			throw new IllegalStateException(
					"Not enough arguments in method to satisfy binding of returning and throwing variables");
		}

		try {
			int algorithmicStep = STEP_JOIN_POINT_BINDING;
			while (this.numberOfRemainingUnboundArguments > 0 && algorithmicStep < STEP_FINISHED) {
				switch (algorithmicStep++) {
					case STEP_JOIN_POINT_BINDING -> {
						if (!maybeBindThisJoinPoint()) {
							maybeBindThisJoinPointStaticPart();
						}
					}
					case STEP_THROWING_BINDING -> maybeBindThrowingVariable();
					case STEP_ANNOTATION_BINDING -> maybeBindAnnotationsFromPointcutExpression();
					case STEP_RETURNING_BINDING -> maybeBindReturningVariable();
					case STEP_PRIMITIVE_ARGS_BINDING -> maybeBindPrimitiveArgsFromPointcutExpression();
					case STEP_THIS_TARGET_ARGS_BINDING -> maybeBindThisOrTargetOrArgsFromPointcutExpression();
					case STEP_REFERENCE_PCUT_BINDING -> maybeBindReferencePointcutParameter();

View on GitHub (pinned to 69bf83ad71)