{"record":{"id":"7426805b0880da47","repo":"Netflix/Hystrix","slug":"sub-type-at-nesting-level-d-of-s-is-expected-to","errorCode":null,"errorMessage":"Sub type at nesting level %d of %s is expected to be generic","messagePattern":"Sub type at nesting level (.+?) of (.+?) is expected to be generic","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":313,"sourceCode":"        }\n    }\n\n    private static Method getAjcMethodFromTarget(JoinPoint joinPoint) {\n        return getAjcMethodAroundAdvice(joinPoint.getTarget().getClass(), (MethodSignature) joinPoint.getSignature());\n    }\n\n\n    private static Class<?> getFirstGenericParameter(Type type) {\n        return getFirstGenericParameter(type, 1);\n    }\n\n    private static Class<?> getFirstGenericParameter(final Type type, final int nestedDepth) {\n        int cDepth = 0;\n        Type tType = type;\n\n        for (int cDept = 0; cDept < nestedDepth; cDept++) {\n            if (!(tType instanceof ParameterizedType))\n                throw new IllegalStateException(String.format(\"Sub type at nesting level %d of %s is expected to be generic\", cDepth, type));\n            tType = ((ParameterizedType) tType).getActualTypeArguments()[cDept];\n        }\n\n        if (tType instanceof ParameterizedType)\n            return (Class<?>) ((ParameterizedType) tType).getRawType();\n        else if (tType instanceof Class)\n            return (Class<?>) tType;\n\n        throw new UnsupportedOperationException(\"Unsupported type \" + tType);\n    }\n\n    private static MetaHolder.Builder setDefaultProperties(MetaHolder.Builder builder, Class<?> declaringClass, final ProceedingJoinPoint joinPoint) {\n        Optional<DefaultProperties> defaultPropertiesOpt = AopUtils.getAnnotation(joinPoint, DefaultProperties.class);\n        builder.defaultGroupKey(declaringClass.getSimpleName());\n        if (defaultPropertiesOpt.isPresent()) {\n            DefaultProperties defaultProperties = defaultPropertiesOpt.get();\n            builder.defaultProperties(defaultProperties);\n            if (StringUtils.isNotBlank(defaultProperties.groupKey())) {","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/Netflix/Hystrix/blob/5ce3bc58c38e7ca60ef2fe0e516e390e294ad941/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java#L295-L331","documentation":"Thrown by getFirstGenericParameter while walking generic types for a @HystrixCollapser pair: at the requested nesting depth the Type is not a ParameterizedType. Practically this means a raw type was used where a parameterized one is required — most commonly a batch method declared with a raw List (no <T>) or an unparameterized Future/Observable return.","triggerScenarios":"Batch method signature public List getUserByIds(List ids) (raw types, e.g. ported pre-generics code); collapser returning raw Future; any place Javanica must read the element type of List/Future/Observable but the declaration omitted type arguments. The 'cDepth' in the message is always 0 (a known cosmetic bug — the loop variable cDept is never printed), which can mislead debugging.","commonSituations":"Migrating legacy Java 1.4-style service methods into collapsers; IDE raw-type warnings ignored; decompiled or generated code dropping generic signatures.","solutions":["Parameterize the batch method: public List<User> getUserByIds(List<String> ids) instead of raw List.","Parameterize the collapser's return type: Future<User>, not raw Future.","Treat raw-type compiler warnings on collapser/batch methods as errors in your build."],"exampleFix":"// before\n@HystrixCommand\npublic List getUserByIds(List ids) { ... }\n\n// after\n@HystrixCommand\npublic List<User> getUserByIds(List<String> ids) { ... }","handlingStrategy":"validation","validationCode":"static void assertParameterizedTypes(Method collapser, Method batch) {\n    if (!(batch.getGenericParameterTypes()[0] instanceof java.lang.reflect.ParameterizedType))\n        throw new IllegalStateException(batch + \" uses raw List — parameterize it\");\n    java.lang.reflect.Type rt = collapser.getGenericReturnType();\n    if (rt instanceof Class && collapser.getReturnType() == java.util.concurrent.Future.class)\n        throw new IllegalStateException(collapser + \" returns raw Future — parameterize it\");\n}","typeGuard":null,"tryCatchPattern":"Catch IllegalStateException during collapser wiring tests; message 'expected to be generic' (nesting level always prints 0 due to a display bug) → fix the raw type declaration; rethrow otherwise.","preventionTips":["Enable -Xlint:rawtypes and treat raw-type warnings on collapser/batch methods as build errors.","Reflection-based CI test asserting all List/Future/Observable declarations in collapser pairs are parameterized."],"tags":["hystrix","javanica","collapser","generics","raw-type"],"backgroundTag":null,"analyzedSha":"5ce3bc58c38e7ca60ef2fe0e516e390e294ad941","analyzedAt":"2026-08-14T10:55:35.600Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}