{"record":{"id":"cf072e3877880958","repo":"Netflix/Hystrix","slug":"required-batch-method-for-collapser-is-absent-wro","errorCode":null,"errorMessage":"required batch method for collapser is absent, wrong generic type: expected {}.{}(java.util.List<{}>), but it's {}","messagePattern":"required batch method for collapser is absent, wrong generic type: expected (.+?)\\.(.+?)\\(java\\.util\\.List<(.+?)>\\), but it's (.+?)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java","lineNumber":213,"sourceCode":"        @Override\n        public MetaHolder create(Object proxy, Method collapserMethod, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {\n            HystrixCollapser hystrixCollapser = collapserMethod.getAnnotation(HystrixCollapser.class);\n            if (collapserMethod.getParameterTypes().length > 1 || collapserMethod.getParameterTypes().length == 0) {\n                throw new IllegalStateException(\"Collapser method must have one argument: \" + collapserMethod);\n            }\n\n            Method batchCommandMethod = getDeclaredMethod(obj.getClass(), hystrixCollapser.batchMethod(), List.class);\n\n            if (batchCommandMethod == null)\n                throw new IllegalStateException(\"batch method is absent: \" + hystrixCollapser.batchMethod());\n\n            Class<?> batchReturnType = batchCommandMethod.getReturnType();\n            Class<?> collapserReturnType = collapserMethod.getReturnType();\n            boolean observable = collapserReturnType.equals(Observable.class);\n\n            if (!collapserMethod.getParameterTypes()[0]\n                    .equals(getFirstGenericParameter(batchCommandMethod.getGenericParameterTypes()[0]))) {\n                throw new IllegalStateException(\"required batch method for collapser is absent, wrong generic type: expected \"\n                        + obj.getClass().getCanonicalName() + \".\" +\n                        hystrixCollapser.batchMethod() + \"(java.util.List<\" + collapserMethod.getParameterTypes()[0] + \">), but it's \" +\n                        getFirstGenericParameter(batchCommandMethod.getGenericParameterTypes()[0]));\n            }\n\n            final Class<?> collapserMethodReturnType = getFirstGenericParameter(\n                    collapserMethod.getGenericReturnType(),\n                    Future.class.isAssignableFrom(collapserReturnType) || Observable.class.isAssignableFrom(collapserReturnType) ? 1 : 0);\n\n            Class<?> batchCommandActualReturnType = getFirstGenericParameter(batchCommandMethod.getGenericReturnType());\n            if (!collapserMethodReturnType\n                    .equals(batchCommandActualReturnType)) {\n                throw new IllegalStateException(\"Return type of batch method must be java.util.List parametrized with corresponding type: expected \" +\n                        \"(java.util.List<\" + collapserMethodReturnType + \">)\" + obj.getClass().getCanonicalName() + \".\" +\n                        hystrixCollapser.batchMethod() + \"(java.util.List<\" + collapserMethod.getParameterTypes()[0] + \">), but it's \" +\n                        batchCommandActualReturnType);\n            }\n","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/Netflix/Hystrix/blob/5ce3bc58c38e7ca60ef2fe0e516e390e294ad941/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java#L195-L231","documentation":"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.","triggerScenarios":"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).","commonSituations":"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>.","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."],"exampleFix":"// before\n@HystrixCollapser(batchMethod = \"getUserByIds\")\npublic Future<User> getUserById(Long id) { ... }\n@HystrixCommand\npublic List<User> getUserByIds(List<String> ids) { ... }  // mismatch\n\n// after\n@HystrixCommand\npublic List<User> getUserByIds(List<Long> ids) { ... }","handlingStrategy":"validation","validationCode":"static void assertBatchElementType(Class<?> clazz, Method collapser, Method batch) {\n    Class<?> expected = collapser.getParameterTypes()[0];\n    Type p = batch.getGenericParameterTypes()[0];\n    if (!(p instanceof java.lang.reflect.ParameterizedType))\n        throw new IllegalStateException(batch + \" parameter must be a parameterized List\");\n    Class<?> actual = (Class<?>) ((java.lang.reflect.ParameterizedType) p).getActualTypeArguments()[0];\n    if (!expected.equals(actual))\n        throw new IllegalStateException(\"batch List element \" + actual + \" != collapser param \" + expected);\n}","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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)."],"tags":["hystrix","javanica","collapser","generics","batch-method"],"backgroundTag":null,"analyzedSha":"5ce3bc58c38e7ca60ef2fe0e516e390e294ad941","analyzedAt":"2026-08-14T10:55:35.600Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}