Netflix/Hystrix · error · IllegalStateException
batch method must be annotated with HystrixCommand annotatio
Error message
batch method must be annotated with HystrixCommand annotation
What it means
The batch method referenced by @HystrixCollapser(batchMethod = ...) must itself be annotated with @HystrixCommand, because the batch execution is what actually runs as a Hystrix command (the collapser only batches requests). Javanica reads @HystrixCommand off the batch method to build the underlying command's properties, fallback, etc., and throws IllegalStateException when it is missing.
Source
Thrown at hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java:234
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);
}
HystrixCommand hystrixCommand = batchCommandMethod.getAnnotation(HystrixCommand.class);
if (hystrixCommand == null) {
throw new IllegalStateException("batch method must be annotated with HystrixCommand annotation");
}
// method of batch hystrix command must be passed to metaholder because basically collapser doesn't have any actions
// that should be invoked upon intercepted method, it's required only for underlying batch command
MetaHolder.Builder builder = metaHolderBuilder(proxy, batchCommandMethod, obj, args, joinPoint);
if (isCompileWeaving()) {
builder.ajcMethod(getAjcMethodAroundAdvice(obj.getClass(), batchCommandMethod.getName(), List.class));
}
builder.hystrixCollapser(hystrixCollapser);
builder.defaultCollapserKey(collapserMethod.getName());
builder.collapserExecutionType(ExecutionType.getExecutionType(collapserReturnType));
builder.defaultCommandKey(batchCommandMethod.getName());
builder.hystrixCommand(hystrixCommand);
builder.executionType(ExecutionType.getExecutionType(batchReturnType));
builder.observable(observable);View on GitHub (pinned to 5ce3bc58c3)
Solutions
- Add @HystrixCommand to the batch method.
- Verify the import is com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand.
- Keep collapser and batch method in the same class and review both when refactoring.
Example fix
// before
@HystrixCollapser(batchMethod = "getUserByIds")
public Future<User> getUserById(String id) { ... }
public List<User> getUserByIds(List<String> ids) { ... }
// after
@HystrixCollapser(batchMethod = "getUserByIds")
public Future<User> getUserById(String id) { ... }
@HystrixCommand
public List<User> getUserByIds(List<String> ids) { ... } Defensive patterns
Strategy: validation
Validate before calling
static void assertBatchCommandAnnotated(Class<?> clazz, HystrixCollapser c) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals(c.batchMethod()) && m.getParameterTypes().length == 1
&& m.getParameterTypes()[0] == java.util.List.class) {
if (!m.isAnnotationPresent(HystrixCommand.class))
throw new IllegalStateException(m + " must be annotated with @HystrixCommand");
return;
}
}
} Try / catch
Catch IllegalStateException at first collapser invocation in integration tests; 'batch method must be annotated' means the wiring is incomplete — fail fast, never catch-and-continue in production paths.
Prevention
- Copy the canonical collapser/batch pair from the javanica docs whenever adding one — batch method always carries @HystrixCommand.
- Add a startup/CI reflection test validating the annotation on every batch method.
When it happens
Trigger: batchMethod points at a public method that has no @HystrixCommand annotation (only the collapser method is annotated); the annotation was removed during refactoring; the annotation imported is from a different package (e.g. a custom or old com.netflix.hystrix.javanica 3.x artifact).
Common situations: Annotating just the collapser assuming it is enough; IDE auto-import resolving HystrixCommand from an unexpected package; annotation stripped when moving the batch method into another class.
Related errors
- method cannot be annotated with HystrixCommand and HystrixCo
- Collapser method must have one argument: {}
- batch method is absent: {}
- required batch method for collapser is absent, wrong generic
- Return type of batch method must be java.util.List parametri
AI-assisted analysis of Netflix/Hystrix@5ce3bc58c3 (2026-08-14).
Data as JSON: /api/errors/d6e8837f9ecc1e2f.
Report an issue: GitHub.