Netflix/Hystrix · error · IllegalStateException
required batch method for collapser is absent, wrong generic
Error message
required batch method for collapser is absent, wrong generic type: expected {}.{}(java.util.List<{}>), but it's {} What it means
The batch method's single parameter must be a parameterized List whose element type equals the collapser method's parameter type. Javanica extracts the first actual type argument of the batch method's generic parameter (e.g. String from List<String>) and compares it to the collapser's parameter class; a mismatch throws IllegalStateException with a message spelling out the expected signature.
Source
Thrown at hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java:213
@Override
public MetaHolder create(Object proxy, Method collapserMethod, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
HystrixCollapser hystrixCollapser = collapserMethod.getAnnotation(HystrixCollapser.class);
if (collapserMethod.getParameterTypes().length > 1 || collapserMethod.getParameterTypes().length == 0) {
throw new IllegalStateException("Collapser method must have one argument: " + collapserMethod);
}
Method batchCommandMethod = getDeclaredMethod(obj.getClass(), hystrixCollapser.batchMethod(), List.class);
if (batchCommandMethod == null)
throw new IllegalStateException("batch method is absent: " + hystrixCollapser.batchMethod());
Class<?> batchReturnType = batchCommandMethod.getReturnType();
Class<?> collapserReturnType = collapserMethod.getReturnType();
boolean observable = collapserReturnType.equals(Observable.class);
if (!collapserMethod.getParameterTypes()[0]
.equals(getFirstGenericParameter(batchCommandMethod.getGenericParameterTypes()[0]))) {
throw new IllegalStateException("required batch method for collapser is absent, wrong generic type: expected "
+ obj.getClass().getCanonicalName() + "." +
hystrixCollapser.batchMethod() + "(java.util.List<" + collapserMethod.getParameterTypes()[0] + ">), but it's " +
getFirstGenericParameter(batchCommandMethod.getGenericParameterTypes()[0]));
}
final Class<?> collapserMethodReturnType = getFirstGenericParameter(
collapserMethod.getGenericReturnType(),
Future.class.isAssignableFrom(collapserReturnType) || Observable.class.isAssignableFrom(collapserReturnType) ? 1 : 0);
Class<?> batchCommandActualReturnType = getFirstGenericParameter(batchCommandMethod.getGenericReturnType());
if (!collapserMethodReturnType
.equals(batchCommandActualReturnType)) {
throw new IllegalStateException("Return type of batch method must be java.util.List parametrized with corresponding type: expected " +
"(java.util.List<" + collapserMethodReturnType + ">)" + obj.getClass().getCanonicalName() + "." +
hystrixCollapser.batchMethod() + "(java.util.List<" + collapserMethod.getParameterTypes()[0] + ">), but it's " +
batchCommandActualReturnType);
}
View on GitHub (pinned to 5ce3bc58c3)
Solutions
- Make the batch method's List element type exactly equal to the collapser method's parameter type.
- If the type was changed recently, update both signatures in the same commit.
- Avoid raw or wildcard-generic List parameters on the batch method.
Example fix
// before
@HystrixCollapser(batchMethod = "getUserByIds")
public Future<User> getUserById(Long id) { ... }
@HystrixCommand
public List<User> getUserByIds(List<String> ids) { ... } // mismatch
// after
@HystrixCommand
public List<User> getUserByIds(List<Long> ids) { ... } Defensive patterns
Strategy: validation
Validate before calling
static void assertBatchElementType(Class<?> clazz, Method collapser, Method batch) {
Class<?> expected = collapser.getParameterTypes()[0];
Type p = batch.getGenericParameterTypes()[0];
if (!(p instanceof java.lang.reflect.ParameterizedType))
throw new IllegalStateException(batch + " parameter must be a parameterized List");
Class<?> actual = (Class<?>) ((java.lang.reflect.ParameterizedType) p).getActualTypeArguments()[0];
if (!expected.equals(actual))
throw new IllegalStateException("batch List element " + actual + " != collapser param " + expected);
} Try / catch
Fail fast in tests: catch IllegalStateException around the first collapser call and assert the message does not contain 'wrong generic type'; report as a compile-time-style contract violation.
Prevention
- Change collapser parameter type and batch method generic type only together, in one commit.
- CI reflection test that checks List element type == collapser parameter type for every collapser pair (exact class equality).
When it happens
Trigger: Collapser takes Integer but the batch method is batch(List<String>); or the batch parameter is a raw List (no generic argument, which instead trips the 'expected to be generic' error); or the element type is a subclass rather than the exact same class (the check uses Class.equals, not isAssignableFrom).
Common situations: Changing the collapser parameter type (Long ids instead of String) without updating the batch method; using different wrapper types (int vs Integer); batch method declared with a supertype element like List<Object>.
Related errors
- batch method is absent: {}
- Return type of batch method must be java.util.List parametri
- Sub type at nesting level %d of %s is expected to be generic
- Unsupported type {}
- Collapser method must have one argument: {}
AI-assisted analysis of Netflix/Hystrix@5ce3bc58c3 (2026-08-14).
Data as JSON: /api/errors/cf072e3877880958.
Report an issue: GitHub.