spring-projects/spring-framework · error · AopConfigException

Invalid afterThrowing signature: four arguments must be Meth

Error message

Invalid afterThrowing signature: four arguments must be Method, Object[], target, throwable: {method}

What it means

The 4-argument afterThrowing variant must match exactly (Method, Object[], Object, ThrowableSubclass). The validator checks param[0]==Method, param[1]==Object[], param[2] is NOT Throwable (it is the target), and param[3] is a Throwable subclass. Any deviation throws AopConfigException naming the offending method.

Source

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

		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...
				Method existingMethod = this.exceptionHandlerMap.put(throwableParam, method);
				if (existingMethod != null) {
					throw new AopConfigException("Only one afterThrowing method per specific Throwable subclass " +
							"allowed: " + method + " / " + existingMethod);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Found exception handler method on throws advice: " + method);
				}
			}

View on GitHub (pinned to e8729d0438)

Solutions

  1. Use the exact signature: afterThrowing(Method m, Object[] args, Object target, ThrowableSubclass ex).
  2. Ensure the 4th param is a Throwable subclass and the 3rd is the plain target object.
  3. If you only need the exception, drop to the single-arg form afterThrowing(Throwable).

Example fix

// before
public void afterThrowing(Method m, Object[] args, Object target, Object o) {}

// after
public void afterThrowing(Method m, Object[] args, Object target, Exception ex) {}
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : advice.getClass().getMethods()) {
    if (m.getName().equals("afterThrowing") && m.getParameterCount() == 4) {
        Class<?>[] p = m.getParameterTypes();
        if (!Method.class.equals(p[0]) || !Object[].class.equals(p[1])
                || Throwable.class.equals(p[2]) || !Throwable.class.isAssignableFrom(p[3])) {
            throw new IllegalStateException("Bad 4-arg afterThrowing signature: " + m);
        }
    }
}

Prevention

When it happens

Trigger: Defining afterThrowing(Method, Object[], Throwable, Object) (swapped last two); afterThrowing(Method, Object[], Object, Object) (non-throwable 4th); using String[] instead of Object[]; target param typed as Throwable.

Common situations: Misremembering the parameter order; using a concrete non-throwable type for the 4th slot; IDE auto-importing the wrong Method class.

Related errors


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/065fe04c3952a0d0.json. Report an issue: GitHub.