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
- Make your subclass extend TypeToken directly, not another TypeToken subclass.
- Use TypeToken.getParameterized(...) to construct the type without subclassing.
- 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
- Never subclass a class that already extends TypeToken.
- Use TypeToken.getParameterized(...) for dynamic/composed types.
- Review token hierarchies during code review.
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
- TypeToken must be created with a type argument: new TypeToke
- Primitive type is not allowed
- Must specify owner type for {rawType}
- At most one lower bound is supported
- Exactly one upper bound must be specified
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/196f79be63689f00.json.
Report an issue: GitHub.