spring-projects/spring-framework · error · IllegalStateException

Binding of throwing parameter

Error message

Binding of throwing parameter '{}' could not be completed as no available arguments are a subtype of Throwable

What it means

Thrown by maybeBindThrowingVariable (line 350-353) when a throwingName is set but NONE of the unbound advice parameters is a subtype of Throwable. After-throwing advice binds the thrown exception to a parameter; if no parameter can hold a Throwable, binding is impossible. Raised as IllegalStateException (suppressed unless raiseExceptions, but AbstractAspectJAdvice sets raiseExceptions=true at line 461).

Solutions

  1. Declare at least one advice parameter whose type is a Throwable subtype (e.g. Throwable, RuntimeException, or the specific exception).
  2. Ensure the throwing name matches that parameter (or compile with -parameters).
  3. If the method needs non-exception context too, add the Throwable parameter alongside it.

Example fix

// before — no Throwable parameter to bind the thrown exception
@AfterThrowing(pointcut="execution(* svc.*(..))", throwing="ex")
public void onError(String message) { ... }

// after — add a Throwable-typed parameter
@AfterThrowing(pointcut="execution(* svc.*(..))", throwing="ex")
public void onError(Throwable ex) { ... }
Defensive patterns

Strategy: validation

Validate before calling

import java.lang.reflect.Method;

// Ensure at least one Throwable parameter exists before setting throwing.
void assertHasThrowableParam(Method adviceMethod) {
    for (Class<?> t : adviceMethod.getParameterTypes()) {
        if (Throwable.class.isAssignableFrom(t)) return;
    }
    throw new IllegalStateException("after-throwing advice needs a Throwable-typed parameter");
}

Type guard

boolean hasThrowableParam(Method m) {
    for (Class<?> t : m.getParameterTypes()) {
        if (Throwable.class.isAssignableFrom(t)) return true;
    }
    return false;
}

Try / catch

try {
    advice.getPointcut();
} catch (IllegalStateException ex) {
    if (ex.getMessage().contains("no available arguments are a subtype of Throwable")) {
        // add a Throwable-typed parameter to the advice method
    } else { throw ex; }
}

Prevention

When it happens

Trigger: An after-throwing advice declares throwing="ex" but its parameters are all non-Throwable (e.g. after(String msg, Integer code)). Or the only Throwable parameter was already bound to something else (e.g. JoinPoint consumed the slot).

Common situations: Declaring an after-throwing advice without a Throwable-typed parameter, or binding the wrong parameter type. Common when converting an after-returning advice to after-throwing and forgetting to change the parameter type, or when the throwing attribute is set but the method signature lacks an exception parameter.

Related errors


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

Appendix: source

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

		// So there is binding work to do...
		int throwableIndex = -1;
		for (int i = 0; i < this.argumentTypes.length; i++) {
			if (isUnbound(i) && isSubtypeOf(Throwable.class, i)) {
				if (throwableIndex == -1) {
					throwableIndex = i;
				}
				else {
					// Second candidate we've found - ambiguous binding
					throw new AmbiguousBindingException("Binding of throwing parameter '" +
							this.throwingName + "' is ambiguous: could be bound to argument " +
							throwableIndex + " or " + i);
				}
			}
		}

		if (throwableIndex == -1) {
			throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName +
					"' could not be completed as no available arguments are a subtype of Throwable");
		}
		else {
			bindParameterName(throwableIndex, this.throwingName);
		}
	}

	/**
	 * If a returning variable was specified and there is only one choice remaining, bind it.
	 */
	private void maybeBindReturningVariable() {
		if (this.numberOfRemainingUnboundArguments == 0) {
			throw new IllegalStateException(
					"Algorithm assumes that there must be at least one unbound parameter on entry to this method");
		}

		if (this.returningName != null) {
			if (this.numberOfRemainingUnboundArguments > 1) {

View on GitHub (pinned to 69bf83ad71)