{"record":{"id":"b96fc7a33b49e651","repo":"google/gson","slug":"typetoken-type-argument-must-not-contain-a-type-va","errorCode":null,"errorMessage":"TypeToken type argument must not contain a type variable; captured type variable {} declared by {}\nSee {}","messagePattern":"TypeToken type argument must not contain a type variable; captured type variable (.+?) declared by (.+?)\nSee (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/reflect/TypeToken.java","lineNumber":124,"sourceCode":"      }\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) {\n        verifyNoTypeVariable(ownerType);\n      }\n\n      for (Type typeArgument : parameterizedType.getActualTypeArguments()) {\n        verifyNoTypeVariable(typeArgument);\n      }","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/reflect/TypeToken.java#L106-L142","documentation":"Thrown by TypeToken.verifyNoTypeVariable when the captured type argument contains a TypeVariable (e.g. T). Because of erasure, Gson has no runtime type for a type variable, so capturing one (like `new TypeToken<List<T>>(){}` inside a generic method) would silently degrade to a raw type and cause ClassCastException later. The check is skipped only if system property gson.allowCapturingTypeVariables=true.","triggerScenarios":"Inside a generic method/class, writing `new TypeToken<List<T>>(){}` (or `new TypeToken<T>(){}`) where T is a type variable of the enclosing generic scope. The anonymous subclass captures T, which verifyNoTypeVariable rejects recursively across arrays, parameterized types, wildcards, and owner types.","commonSituations":"Writing generic helper methods like `<T> TypeToken<List<T>> token()` that try to capture T; builders/factories parameterized by T that build TypeTokens; refactoring that accidentally widens a type variable into a TypeToken; migrating from raw types to generics and forgetting T cannot be captured.","solutions":["Pass the Class<T> (or Type) explicitly and construct the TypeToken at runtime via TypeToken.getParameterized(List.class, elementClass).","Accept that the type argument must be statically known; restructure so the caller supplies a concrete TypeToken.","Only as a last resort, set -Dgson.allowCapturingTypeVariables=true to disable the check (you accept the runtime ClassCastException risk and loss of type-safety)."],"exampleFix":"// before (broken)\npublic <T> TypeToken<List<T>> listToken() {\n  return new TypeToken<List<T>>() {}; // captures T -> throws\n}\n\n// after: pass Class<T> and build at runtime\npublic <T> TypeToken<List<T>> listToken(Class<T> elementType) {\n  return (TypeToken<List<T>>) (TypeToken<?>) TypeToken.getParameterized(List.class, elementType);\n}\n\n// usage:\nTypeToken<List<String>> t = listToken(String.class);","handlingStrategy":"validation","validationCode":"// prevent capture at compile time: refuse generic methods returning TypeToken<T>\n// prefer passing Class<T> explicitly:\npublic <T> TypeToken<List<T>> listOf(Class<T> cls) {\n  return (TypeToken<List<T>>) (TypeToken<?>) TypeToken.getParameterized(List.class, cls);\n}","typeGuard":"public static <T> boolean capturesTypeVariable(TypeToken<T> t) {\n  // best-effort: type variables appear in toString as their name without a package\n  String s = t.getType().toString();\n  return s.matches(\".*\\\\b[T-Z]\\\\b.*\") && !(t.getType() instanceof Class);\n}","tryCatchPattern":"try {\n  return new TypeToken<List<T>>() {}; // inside generic method\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"TypeToken type argument must not contain a type variable\")) {\n    // fall back to runtime construction with the actual Class\n    return (TypeToken<List<T>>) (TypeToken<?>) TypeToken.getParameterized(List.class, elementType);\n  }\n  throw e;\n}","preventionTips":["Never write `new TypeToken<...T...>(){}` where T is a generic parameter.","Pass Class<T>/Type explicitly and build with TypeToken.getParameterized.","Static-analysis: warn on any TypeToken instantiation inside a generic method/class.","Avoid -Dgson.allowCapturingTypeVariables=true in production; it just defers the failure to a ClassCastException."],"tags":["gson","type-token","generics","type-erasure","type-variable","illegal-argument"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}