Netflix/Hystrix · error · IllegalStateException

batch method is absent: {}

Error message

batch method is absent: {}

What it means

The @HystrixCollapser annotation names a batchMethod, and Javanica looks it up on the same class via getDeclaredMethod(clazz, batchMethod, List.class). If no method with that exact name and a single java.util.List parameter exists (declared on the class, not inherited), an IllegalStateException is thrown. Note it uses getDeclaredMethod, so the batch method must be declared directly on the target class.

Source

Thrown at hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java:205

            setFallbackMethod(builder, obj.getClass(), method);
            builder = setDefaultProperties(builder, obj.getClass(), joinPoint);
            return builder;
        }
    }

    private static class CollapserMetaHolderFactory extends MetaHolderFactory {

        @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());

View on GitHub (pinned to 5ce3bc58c3)

Solutions

  1. Declare a method with the exact batchMethod name on the same class taking exactly one java.util.List parameter.
  2. Check spelling/case of the batchMethod attribute against the real method name.
  3. If the batch method lives in a superclass, move or override it on the collapser's class (lookup uses getDeclaredMethod).

Example fix

// before
@HystrixCollapser(batchMethod = "getUserByIds")
public Future<User> getUserById(String id) { ... }
// no matching method, or: public List<User> getUserByIds(Collection<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 assertBatchMethodExists(Class<?> clazz, HystrixCollapser c) {
    boolean found = false;
    for (Method m : clazz.getDeclaredMethods()) {
        if (m.getName().equals(c.batchMethod())
                && m.getParameterTypes().length == 1
                && m.getParameterTypes()[0] == java.util.List.class) { found = true; break; }
    }
    if (!found) throw new IllegalStateException("batch method missing: " + clazz.getName() + "." + c.batchMethod() + "(List)");
}

Try / catch

Startup self-test: invoke each collapser once in a test context inside try/catch (IllegalStateException) and surface the message; treat any 'batch method is absent' as build failure.

Prevention

When it happens

Trigger: @HystrixCollapser(batchMethod = "foo") where foo does not exist, is misspelled, has a different name, or exists but does not take exactly one List parameter (e.g. takes Collection, varargs, or List plus another arg). Also thrown when the batch method is inherited from a superclass rather than declared on the class.

Common situations: Renaming the batch method without updating batchMethod; batch method defined in an interface or superclass; batch method parameter typed as Collection<String> or ArrayList instead of List.

Related errors


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