spring-projects/spring-framework · error · AopConfigException
Only one afterThrowing method per specific Throwable subclas
Error message
Only one afterThrowing method per specific Throwable subclass allowed: {method} / {existingMethod} What it means
exceptionHandlerMap keys handlers by Throwable subclass. If two afterThrowing methods accept the same (or equal) Throwable subclass, the second registration evicts the first and Spring throws AopConfigException naming both the new and existing methods - dispatch would otherwise be ambiguous.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java:109
}
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);
}
}
}
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.
*/View on GitHub (pinned to e8729d0438)
Solutions
- Keep exactly one afterThrowing method per Throwable subclass (combine logic into a single method).
- If you need both 1-arg and 4-arg variants, key them on different exception subclasses.
- Remove duplicate handlers from superclasses or the class itself.
Example fix
// before
public void afterThrowing(IOException ex) { ... }
public void afterThrowing(Method m, Object[] a, Object t, IOException ex) { ... } // duplicate key
// after: keep one, branch inside
public void afterThrowing(Method m, Object[] a, Object t, IOException ex) { ... } Defensive patterns
Strategy: validation
Validate before calling
java.util.Map<Class<?>, Long> counts = new java.util.HashMap<>();
for (Method m : advice.getClass().getMethods()) {
if (!m.getName().equals("afterThrowing")) continue;
Class<?> exType = m.getParameterCount() == 1 ? m.getParameterTypes()[0]
: m.getParameterCount() == 4 ? m.getParameterTypes()[3] : null;
if (exType != null) counts.merge(exType, 1L, Long::sum);
}
if (counts.values().stream().anyMatch(c -> c > 1)) {
throw new IllegalStateException("Duplicate afterThrowing for the same Throwable subclass in " + advice.getClass());
} Prevention
- Keep one afterThrowing method per Throwable subclass.
- Combine related logic into a single handler rather than overloading.
- Watch for inherited afterThrowing methods that duplicate subclass ones.
When it happens
Trigger: Defining two afterThrowing(Exception) methods, or afterThrowing(Method,Object[],Object,IOException) and afterThrowing(IOException) - both keyed by IOException - on the same advice class.
Common situations: Inheritance where a subclass redefines a parent afterThrowing for the same exception; copy-paste handlers; refactoring that introduced a duplicate.
Related errors
- Invalid afterThrowing signature: single argument must be a T
- Invalid afterThrowing signature: four arguments must be Meth
- Unsupported afterThrowing signature: single throwable argume
- At least one handler method must be found in class [{throwsA
- Target required after globals
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/d76edfc760d22cda.json.
Report an issue: GitHub.