Netflix/Hystrix · error · UnsupportedOperationException

Unsupported type {}

Error message

Unsupported type {}

What it means

The terminal fallback of getFirstGenericParameter: after resolving the requested nesting depth, the resulting Type is neither a ParameterizedType nor a Class. Java reflection can return TypeVariable (unresolved generic like <T>), WildcardType (? extends X), or GenericArrayType, none of which Javanica can reduce to a concrete Class for building the collapser, so it throws UnsupportedOperationException.

Source

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

        return getFirstGenericParameter(type, 1);
    }

    private static Class<?> getFirstGenericParameter(final Type type, final int nestedDepth) {
        int cDepth = 0;
        Type tType = type;

        for (int cDept = 0; cDept < nestedDepth; cDept++) {
            if (!(tType instanceof ParameterizedType))
                throw new IllegalStateException(String.format("Sub type at nesting level %d of %s is expected to be generic", cDepth, type));
            tType = ((ParameterizedType) tType).getActualTypeArguments()[cDept];
        }

        if (tType instanceof ParameterizedType)
            return (Class<?>) ((ParameterizedType) tType).getRawType();
        else if (tType instanceof Class)
            return (Class<?>) tType;

        throw new UnsupportedOperationException("Unsupported type " + tType);
    }

    private static MetaHolder.Builder setDefaultProperties(MetaHolder.Builder builder, Class<?> declaringClass, final ProceedingJoinPoint joinPoint) {
        Optional<DefaultProperties> defaultPropertiesOpt = AopUtils.getAnnotation(joinPoint, DefaultProperties.class);
        builder.defaultGroupKey(declaringClass.getSimpleName());
        if (defaultPropertiesOpt.isPresent()) {
            DefaultProperties defaultProperties = defaultPropertiesOpt.get();
            builder.defaultProperties(defaultProperties);
            if (StringUtils.isNotBlank(defaultProperties.groupKey())) {
                builder.defaultGroupKey(defaultProperties.groupKey());
            }
            if (StringUtils.isNotBlank(defaultProperties.threadPoolKey())) {
                builder.defaultThreadPoolKey(defaultProperties.threadPoolKey());
            }
        }
        return builder;
    }

View on GitHub (pinned to 5ce3bc58c3)

Solutions

  1. Bind generics to concrete classes on collapser/batch methods (no <T>, no wildcards): Future<User> getUserById(String id).
  2. Do not put @HystrixCollapser on generic base-class methods; write a concrete leaf-class method per collapsed operation.
  3. Replace List<? extends User> with List<User> on the batch method.

Example fix

// before
abstract class BaseSvc<T> {
    @HystrixCollapser(batchMethod = "getAll")
    public abstract Future<T> getById(String id);
}

// after
abstract class BaseSvc<T> {
    protected abstract List<T> getAll(List<String> ids);
}
class UserSvc extends BaseSvc<User> {
    @HystrixCollapser(batchMethod = "getAll")
    public Future<User> getById(String id) { ... }
    @HystrixCommand
    public List<User> getAll(List<String> ids) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

static boolean isResolvableToClass(java.lang.reflect.Type t) {
    return t instanceof Class || (t instanceof java.lang.reflect.ParameterizedType
            && ((java.lang.reflect.ParameterizedType) t).getRawType() instanceof Class);
}
static void assertConcreteGenerics(Method collapser, Method batch) {
    java.lang.reflect.Type elem = ((java.lang.reflect.ParameterizedType) batch.getGenericParameterTypes()[0]).getActualTypeArguments()[0];
    if (!isResolvableToClass(elem)) throw new IllegalStateException("unresolvable generic on " + batch + ": " + elem);
}

Try / catch

try/catch UnsupportedOperationException with message 'Unsupported type' during collapser setup → log the offending Type (TypeVariable/wildcard) and fail the build; do not retry at runtime.

Prevention

When it happens

Trigger: Collapser or batch method declared with unresolved type variables, e.g. <T> Future<T> getById(T id) or List<? extends User>; a generic base class exposing the batch method where T is never bound to a concrete class at the call site.

Common situations: Generic repository/base-service patterns reused for collapsers; wildcard collection types pulled in from shared API interfaces.

Related errors


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