{"id":"196f79be63689f00","repo":"google/gson","slug":"must-only-create-direct-subclasses-of-typetoken","errorCode":null,"errorMessage":"Must only create direct subclasses of TypeToken","messagePattern":"Must only create direct subclasses of TypeToken","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/reflect/TypeToken.java","lineNumber":118,"sourceCode":"        Type typeArgument = GsonTypes.canonicalize(parameterized.getActualTypeArguments()[0]);\n\n        if (isCapturingTypeVariablesForbidden()) {\n          verifyNoTypeVariable(typeArgument);\n        }\n        return typeArgument;\n      }\n    }\n    // Check for raw TypeToken as superclass\n    else if (superclass == TypeToken.class) {\n      throw new IllegalStateException(\n          \"TypeToken must be created with a type argument: new TypeToken<...>() {}; When using code\"\n              + \" shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved.\"\n              + \"\\nSee \"\n              + TroubleshootingGuide.createUrl(\"type-token-raw\"));\n    }\n\n    // User created subclass of subclass of TypeToken\n    throw new IllegalStateException(\"Must only create direct subclasses of TypeToken\");\n  }\n\n  private static void verifyNoTypeVariable(Type type) {\n    if (type instanceof TypeVariable) {\n      TypeVariable<?> typeVariable = (TypeVariable<?>) type;\n      throw new IllegalArgumentException(\n          \"TypeToken type argument must not contain a type variable; captured type variable \"\n              + typeVariable.getName()\n              + \" declared by \"\n              + typeVariable.getGenericDeclaration()\n              + \"\\nSee \"\n              + TroubleshootingGuide.createUrl(\"typetoken-type-variable\"));\n    } else if (type instanceof GenericArrayType) {\n      verifyNoTypeVariable(((GenericArrayType) type).getGenericComponentType());\n    } else if (type instanceof ParameterizedType) {\n      ParameterizedType parameterizedType = (ParameterizedType) type;\n      Type ownerType = parameterizedType.getOwnerType();\n      if (ownerType != null) {","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/reflect/TypeToken.java#L100-L136","documentation":"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'.","triggerScenarios":"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.","commonSituations":"Building reusable base token helpers, framework abstractions over TypeToken, or migrating code that wraps TypeToken hierarchies.","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."],"exampleFix":"// before\nclass ListToken<E> extends TypeToken<List<E>> {}\nclass StringListToken extends ListToken<String> {} // indirect -> throws\n\n// after\nclass StringListToken extends TypeToken<List<String>> {} // direct\n// or, no subclassing:\nTypeToken<List<String>> t = TypeToken.getParameterized(List.class, String.class);","handlingStrategy":"validation","validationCode":"boolean isDirectTypeTokenSubclass(Class<?> c) {\n  return c.getSuperclass() == TypeToken.class;\n}","typeGuard":"static boolean isDirectSubclassOfTypeToken(Class<?> c) {\n  return c != null && c.getSuperclass() == TypeToken.class;\n}","tryCatchPattern":"// Construction-time failure; prefer static validation/compile-time checks.\ntry {\n  TypeToken<?> t = new MyIndirectToken();\n} catch (IllegalStateException e) {\n  // refactor MyIndirectToken to extend TypeToken directly, or use TypeToken.getParameterized\n}","preventionTips":["Never subclass a class that already extends TypeToken.","Use TypeToken.getParameterized(...) for dynamic/composed types.","Review token hierarchies during code review."],"tags":["gson","type-token","generics","inheritance"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}