spring-projects/spring-framework · error · AopConfigException

At least one handler method must be found in class [{throwsA

Error message

At least one handler method must be found in class [{throwsAdvice.getClass()}]

What it means

After scanning all public methods, if no compliant afterThrowing handler was registered (the map is empty), ThrowsAdviceInterceptor refuses to construct - an exception handler with no handlers is meaningless. AopConfigException is thrown naming the advice class.

Source

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

				}
				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);
				}
			}
		}

		if (this.exceptionHandlerMap.isEmpty()) {
			throw new AopConfigException(
					"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
		}
	}


	/**
	 * Return the number of handler methods in this advice.
	 */
	public int getHandlerMethodCount() {
		return this.exceptionHandlerMap.size();
	}


	@Override
	public @Nullable Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}

View on GitHub (pinned to e8729d0438)

Solutions

  1. Declare at least one public afterThrowing method with a valid 1- or 4-arg signature.
  2. Ensure the method is public (getMethods() returns only public methods).
  3. Check the exact spelling: method must be named 'afterThrowing'.

Example fix

// before
class MyAdvice implements ThrowsAdvice { /* no methods */ }

// after
class MyAdvice implements ThrowsAdvice {
  public void afterThrowing(Exception ex) { log(ex); }
}
Defensive patterns

Strategy: validation

Validate before calling

long n = java.util.Arrays.stream(advice.getClass().getMethods())
    .filter(m -> m.getName().equals("afterThrowing")
        && (m.getParameterCount() == 1 || m.getParameterCount() == 4)
        && java.lang.reflect.Modifier.isPublic(m.getModifiers()))
    .count();
if (n == 0) throw new IllegalStateException(advice.getClass() + " implements ThrowsAdvice but defines no valid afterThrowing method");

Type guard

public static boolean hasValidAfterThrowing(Class<?> adviceClass) {
    return java.util.Arrays.stream(adviceClass.getMethods())
        .anyMatch(m -> m.getName().equals("afterThrowing")
            && (m.getParameterCount() == 1 || m.getParameterCount() == 4));
}

Prevention

When it happens

Trigger: Implementing the marker interface org.springframework.aop.ThrowsAdvice without declaring any afterThrowing method; declaring handlers with invalid signatures (so all were rejected before reaching registration, though those throw earlier) or with the wrong method name.

Common situations: Empty marker implementation used as a placeholder; typo in method name ('afterthrowing', 'after_Throwing'); methods made non-public so getClass().getMethods() does not see them.

Related errors


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