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

  1. Add @HystrixCommand to the batch method.
  2. Verify the import is com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand.
  3. 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

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


AI-assisted analysis of Netflix/Hystrix@5ce3bc58c3 (2026-08-14). Data as JSON: /api/errors/d6e8837f9ecc1e2f. Report an issue: GitHub.