google/gson · error · IllegalStateException

Must only create direct subclasses of TypeToken

Error message

Must only create direct subclasses of TypeToken

What it means

TypeToken.getTypeTokenTypeArgument expects the immediate generic superclass to be TypeToken itself. If you subclass a class that already subclasses TypeToken (an indirect subclass), Gson cannot reliably capture the type argument and throws IllegalStateException 'Must only create direct subclasses of TypeToken'.

Source

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

        Type typeArgument = GsonTypes.canonicalize(parameterized.getActualTypeArguments()[0]);

        if (isCapturingTypeVariablesForbidden()) {
          verifyNoTypeVariable(typeArgument);
        }
        return typeArgument;
      }
    }
    // Check for raw TypeToken as superclass
    else if (superclass == TypeToken.class) {
      throw new IllegalStateException(
          "TypeToken must be created with a type argument: new TypeToken<...>() {}; When using code"
              + " shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved."
              + "\nSee "
              + TroubleshootingGuide.createUrl("type-token-raw"));
    }

    // User created subclass of subclass of TypeToken
    throw new IllegalStateException("Must only create direct subclasses of TypeToken");
  }

  private static void verifyNoTypeVariable(Type type) {
    if (type instanceof TypeVariable) {
      TypeVariable<?> typeVariable = (TypeVariable<?>) type;
      throw new IllegalArgumentException(
          "TypeToken type argument must not contain a type variable; captured type variable "
              + typeVariable.getName()
              + " declared by "
              + typeVariable.getGenericDeclaration()
              + "\nSee "
              + TroubleshootingGuide.createUrl("typetoken-type-variable"));
    } else if (type instanceof GenericArrayType) {
      verifyNoTypeVariable(((GenericArrayType) type).getGenericComponentType());
    } else if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type ownerType = parameterizedType.getOwnerType();
      if (ownerType != null) {

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Make your subclass extend TypeToken directly, not another TypeToken subclass.
  2. Use TypeToken.getParameterized(...) to construct the type without subclassing.
  3. Refactor base token helpers to expose factory methods returning TypeToken instances rather than subclassing.

Example fix

// before
class ListToken<E> extends TypeToken<List<E>> {}
class StringListToken extends ListToken<String> {} // indirect -> throws

// after
class StringListToken extends TypeToken<List<String>> {} // direct
// or, no subclassing:
TypeToken<List<String>> t = TypeToken.getParameterized(List.class, String.class);
Defensive patterns

Strategy: validation

Validate before calling

boolean isDirectTypeTokenSubclass(Class<?> c) {
  return c.getSuperclass() == TypeToken.class;
}

Type guard

static boolean isDirectSubclassOfTypeToken(Class<?> c) {
  return c != null && c.getSuperclass() == TypeToken.class;
}

Try / catch

// Construction-time failure; prefer static validation/compile-time checks.
try {
  TypeToken<?> t = new MyIndirectToken();
} catch (IllegalStateException e) {
  // refactor MyIndirectToken to extend TypeToken directly, or use TypeToken.getParameterized
}

Prevention

When it happens

Trigger: Creating a class 'class StringListToken extends ListToken<String>' where ListToken already extends TypeToken<List<T>>, then instantiating StringListToken; Gson only supports one level of subclassing.

Common situations: Building reusable base token helpers, framework abstractions over TypeToken, or migrating code that wraps TypeToken hierarchies.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/196f79be63689f00.json. Report an issue: GitHub.