spring-projects/spring-framework · error · AopConfigException

Invalid afterThrowing signature: single argument must be a…

Error message

Invalid afterThrowing signature: single argument must be a Throwable subclass

What it means

Thrown as AopConfigException by ThrowsAdviceInterceptor's constructor when an afterThrowing method has exactly one parameter but that parameter is not assignable to Throwable. Per the ThrowsAdvice contract, the single-arg form must be 'void afterThrowing(SomeThrowable ex)'.

Solutions

  1. Change the single argument to a Throwable subclass: 'public void afterThrowing(MyException ex)'.
  2. If you need method/args/target context, use the four-argument form: afterThrowing(Method, Object[], Object, Throwable).

Example fix

// before
public void afterThrowing(String reason) { ... } // non-Throwable

// after
public void afterThrowing(IllegalStateException ex) { ... }
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : advice.getClass().getMethods()) {
  if (m.getName().equals("afterThrowing") && m.getParameterCount() == 1
      && !Throwable.class.isAssignableFrom(m.getParameterTypes()[0])) {
    throw new IllegalStateException("Bad afterThrowing: " + m);
  }
}
new ThrowsAdviceInterceptor(advice);

Type guard

static boolean validSingleArgThrows(Method m) {
  return m.getParameterCount() == 1 && Throwable.class.isAssignableFrom(m.getParameterTypes()[0]);
}

Try / catch

try { new ThrowsAdviceInterceptor(advice); }
catch (AopConfigException e) {
  if (e.getMessage().contains("single argument must be a Throwable")) { /* fix signature */ }
  throw e;
}

Prevention

When it happens

Trigger: A ThrowsAdvice implementation defines 'public void afterThrowing(String msg)' or any non-Throwable single argument; the constructor at lines 84-90 rejects it eagerly while scanning the advice class.

Common situations: Typos in afterThrowing signatures; copy-paste from a different advice style; believing the argument can be a context object instead of an exception.

Related errors


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

Appendix: source

Thrown at spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java:88

	/**
	 * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
	 * @param throwsAdvice the advice object that defines the exception handler methods
	 * (usually a {@link org.springframework.aop.ThrowsAdvice} implementation)
	 */
	public ThrowsAdviceInterceptor(Object throwsAdvice) {
		Assert.notNull(throwsAdvice, "Advice must not be null");
		this.throwsAdvice = throwsAdvice;

		Method[] methods = throwsAdvice.getClass().getMethods();
		for (Method method : methods) {
			if (method.getName().equals(AFTER_THROWING)) {
				Class<?> throwableParam = null;
				if (method.getParameterCount() == 1) {
					// just a Throwable parameter
					throwableParam = method.getParameterTypes()[0];
					if (!Throwable.class.isAssignableFrom(throwableParam)) {
						throw new AopConfigException("Invalid afterThrowing signature: " +
								"single argument must be a Throwable subclass");
					}
				}
				else if (method.getParameterCount() == 4) {
					// Method, Object[], target, throwable
					Class<?>[] paramTypes = method.getParameterTypes();
					if (!Method.class.equals(paramTypes[0]) || !Object[].class.equals(paramTypes[1]) ||
							Throwable.class.equals(paramTypes[2]) || !Throwable.class.isAssignableFrom(paramTypes[3])) {
						throw new AopConfigException("Invalid afterThrowing signature: " +
								"four arguments must be Method, Object[], target, throwable: " + method);
					}
					throwableParam = paramTypes[3];
				}
				if (throwableParam == null) {
					throw new AopConfigException("Unsupported afterThrowing signature: single throwable argument " +
							"or four arguments Method, Object[], target, throwable expected: " + method);
				}
				// An exception handler to register...

View on GitHub (pinned to 69bf83ad71)