OpenFeign/feign · error · IllegalArgumentException

Expected a Class, ParameterizedType, or GenericArrayType…

Error message

Expected a Class, ParameterizedType, or GenericArrayType, but <{type}> is of type {className}

What it means

Types.getRawType() resolves the raw Class behind a java.lang.reflect Type but only understands Class, ParameterizedType, GenericArrayType and WildcardType. Any other Type implementation (or null) triggers this IllegalArgumentException naming the offending type and its class.

Solutions

  1. Pass a Class, ParameterizedType, or GenericArrayType instead of a TypeVariable
  2. Resolve type variables to concrete types first (e.g. via Types.resolve or getGenericSuperclass)
  3. Null-check the Type before calling getRawType
  4. If the type comes from a framework, capture the concrete body type at build time

Example fix

// before
Type t = method.getTypeParameters()[0]; // TypeVariable -> IllegalArgumentException
// after
Type t = method.getGenericReturnType(); // ParameterizedType/Class
Class<?> raw = Types.getRawType(t);
Defensive patterns

Strategy: type-guard

Validate before calling

if (type == null || type instanceof TypeVariable) throw new IllegalArgumentException("Use a concrete Class/ParameterizedType, got: " + type);

Type guard

static boolean isResolvable(Type t) { return t instanceof Class || t instanceof ParameterizedType || t instanceof GenericArrayType || t instanceof WildcardType; }

Try / catch

try { Class<?> raw = Types.getRawType(type); } catch (IllegalArgumentException e) { /* fall back to Types.resolve with declaring context */ }

Prevention

When it happens

Trigger: Passing a TypeVariable, custom Type implementation, or null to Types.getRawType (directly or via encoders/contracts that use it), e.g. feeding a generic method's TypeVariable into encoder type resolution.

Common situations: Custom Encoder/Decoder implementations calling Types.getRawType on unbound type variables; proxy/reflection frameworks producing exotic Type implementations; version changes where types flow differently through the contract.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10). Data as JSON: /api/errors/06291d343ea9bb73. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/feign/Types.java:74

        throw new IllegalArgumentException();
      }
      return (Class<?>) rawType;

    } else if (type instanceof GenericArrayType) {
      Type componentType = ((GenericArrayType) type).getGenericComponentType();
      return Array.newInstance(getRawType(componentType), 0).getClass();

    } else if (type instanceof TypeVariable) {
      // We could use the variable's bounds, but that won't work if there are multiple. Having a raw
      // type that's more general than necessary is okay.
      return Object.class;

    } else if (type instanceof WildcardType) {
      return getRawType(((WildcardType) type).getUpperBounds()[0]);

    } else {
      String className = type == null ? "null" : type.getClass().getName();
      throw new IllegalArgumentException(
          "Expected a Class, ParameterizedType, or "
              + "GenericArrayType, but <"
              + type
              + "> is of type "
              + className);
    }
  }

  /** Returns true if {@code a} and {@code b} are equal. */
  static boolean equals(Type a, Type b) {
    if (a == b) {
      return true; // Also handles (a == null && b == null).

    } else if (a instanceof Class) {
      return a.equals(b); // Class already specifies equals().

    } else if (a instanceof ParameterizedType) {
      if (!(b instanceof ParameterizedType)) {

View on GitHub (pinned to e2a1e27560)