google/gson · error · IllegalArgumentException

rawType must be of type Class, but was {}

Error message

rawType must be of type Class, but was {}

What it means

Thrown by TypeToken.getParameterized when the rawType argument is not an instance of java.lang.Class (e.g. a ParameterizedType, GenericArrayType, TypeVariable, or WildcardType). getParameterized validates rawType explicitly because it is the only public API where users can construct malformed parameterized types; JDK bug JDK-8250659 is referenced.

Source

Thrown at gson/src/main/java/com/google/gson/reflect/TypeToken.java:393

   *
   * As seen here the result is a {@code TypeToken<?>}; this method cannot provide any type-safety,
   * and care must be taken to pass in the correct number of type arguments.
   *
   * <p>If {@code rawType} is a non-generic class and no type arguments are provided, this method
   * simply delegates to {@link #get(Class)} and creates a {@code TypeToken(Class)}.
   *
   * @throws IllegalArgumentException If {@code rawType} is not of type {@code Class}, or if the
   *     type arguments are invalid for the raw type
   */
  public static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) {
    Objects.requireNonNull(rawType);
    Objects.requireNonNull(typeArguments);

    // Perform basic validation here because this is the only public API where users
    // can create malformed parameterized types
    if (!(rawType instanceof Class)) {
      // See also https://bugs.openjdk.org/browse/JDK-8250659
      throw new IllegalArgumentException("rawType must be of type Class, but was " + rawType);
    }
    Class<?> rawClass = (Class<?>) rawType;
    TypeVariable<?>[] typeVariables = rawClass.getTypeParameters();

    int expectedArgsCount = typeVariables.length;
    int actualArgsCount = typeArguments.length;
    if (actualArgsCount != expectedArgsCount) {
      throw new IllegalArgumentException(
          rawClass.getName()
              + " requires "
              + expectedArgsCount
              + " type arguments, but got "
              + actualArgsCount);
    }

    // For legacy reasons create a TypeToken(Class) if the type is not generic
    if (typeArguments.length == 0) {
      return get(rawClass);

View on GitHub (pinned to 310ac341f2)

Solutions

  1. Pass the raw Class (e.g. Map.class, List.class), not a parameterized Type. If you have a Type, get its erasure via GsonTypes.getRawType or Class.forName first.
  2. For nested generics, build from the inside out with getParameterized and pass the result's getType() only as a type argument, never as rawType.
  3. If you already hold a Type, use TypeToken.get(type) instead of getParameterized.
  4. Add a runtime assertion `assert rawType instanceof Class` before calling to fail fast in tests.

Example fix

// before (broken)
Type listType = new TypeToken<List<String>>(){}.getType();
TypeToken<?> t = TypeToken.getParameterized(listType, String.class); // listType not a Class -> throws

// after: pass the raw Class
TypeToken<?> t = TypeToken.getParameterized(List.class, String.class);

// or, if you already have a Type, use TypeToken.get:
TypeToken<?> t = TypeToken.get(listType);

// for Map<String, List<Integer>>:
TypeToken<?> inner = TypeToken.getParameterized(List.class, Integer.class);
TypeToken<?> outer = TypeToken.getParameterized(Map.class, String.class, inner.getType());
Defensive patterns

Strategy: validation

Validate before calling

// guard before calling getParameterized
if (!(rawType instanceof Class)) {
  // either resolve erasure, or use TypeToken.get(rawType)
  rawType = rawType instanceof ParameterizedType
      ? ((ParameterizedType) rawType).getRawType()
      : rawType;
}
if (!(rawType instanceof Class)) {
  throw new IllegalArgumentException("rawType must be Class, got: " + rawType);
}

Type guard

public static boolean isRawClass(Type t) {
  return t instanceof Class;
}

Try / catch

try {
  return TypeToken.getParameterized(rawType, args);
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("rawType must be of type Class")) {
    // fall back: if it's already a resolved Type, use TypeToken.get
    return TypeToken.get(rawType);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling TypeToken.getParameterized(someType, ...) where someType is itself a parameterized type (like List<String>.class equivalent) or a GenericArrayType, rather than a plain Class. Example: TypeToken.getParameterized(new TypeToken<List<String>>(){}.getType(), String.class) passes a ParameterizedType as rawType.

Common situations: Mistakenly passing a Type's toString or a nested generic type where the raw class is expected; chained calls that hand a resolved generic type instead of the erasure; copying patterns from getParameterized examples and feeding the wrong slot; confusion between raw type and parameterized type.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/8364825de424d404. Report an issue: GitHub.