{"record":{"id":"cd1bce9ec511de39","repo":"google/gson","slug":"typetoken-captured-null-as-type-argument-probab","errorCode":null,"errorMessage":"TypeToken captured `null` as type argument; probably a compiler / runtime bug","messagePattern":"TypeToken captured `null` as type argument; probably a compiler / runtime bug","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/reflect/TypeToken.java","lineNumber":155,"sourceCode":"        verifyNoTypeVariable(ownerType);\n      }\n\n      for (Type typeArgument : parameterizedType.getActualTypeArguments()) {\n        verifyNoTypeVariable(typeArgument);\n      }\n    } else if (type instanceof WildcardType) {\n      WildcardType wildcardType = (WildcardType) type;\n      for (Type bound : wildcardType.getLowerBounds()) {\n        verifyNoTypeVariable(bound);\n      }\n      for (Type bound : wildcardType.getUpperBounds()) {\n        verifyNoTypeVariable(bound);\n      }\n    } else if (type == null) {\n      // Occurs in Eclipse IDE and certain Java versions (e.g. Java 11.0.18) when capturing type\n      // variable declared by method of local class, see\n      // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/975\n      throw new IllegalArgumentException(\n          \"TypeToken captured `null` as type argument; probably a compiler / runtime bug\");\n    }\n  }\n\n  /** Returns the raw (non-generic) type for this type. */\n  public final Class<? super T> getRawType() {\n    return rawType;\n  }\n\n  /** Gets underlying {@code Type} instance. */\n  public final Type getType() {\n    return type;\n  }\n\n  /**\n   * Check if this type is assignable from the given class object.\n   *\n   * @deprecated this implementation may be inconsistent with javac for types with wildcards.","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/reflect/TypeToken.java#L137-L173","documentation":"Thrown by TypeToken.verifyNoTypeVariable when a component of the captured type argument is null. This is a known JDK/Eclipse compiler quirk (see eclipse-jdt/eclipse.jdt.core#975, reproduced e.g. on Java 11.0.18) where capturing a type variable declared by a method of a local class yields null instead of a TypeVariable. The message explicitly attributes it to a compiler/runtime bug.","triggerScenarios":"Using the Eclipse JDT compiler (or an affected JDK like 11.0.18) and constructing a TypeToken inside a local class whose method declares a type variable that gets captured. The reflection API returns null where a TypeVariable was expected, tripping the `type == null` branch.","commonSituations":"Eclipse IDE builds producing class files that exhibit the null type-variable bug; specific JDK point releases (11.0.18) with the underlying reflection bug; local/anonymous classes with generic methods; switching between javac and ecj producing different runtime behavior.","solutions":["Build with javac instead of Eclipse ecj (or update Eclipse JDT to a version with the fix for eclipse.jdt.core#975).","Update to a JDK point release that no longer exhibits the null-returning reflection bug.","Refactor to avoid TypeToken capture inside local classes with generic methods; use TypeToken.getParameterized at runtime instead.","If unavoidable, the system property gson.allowCapturingTypeVariables=true bypasses verifyNoTypeVariable entirely (accept the type-safety implications)."],"exampleFix":"// before (broken on affected Eclipse/JDK)\npublic void process() {\n  class Local {\n    <T> TypeToken<T> token() { return new TypeToken<T>() {}; } // null type var on buggy JVM\n  }\n}\n\n// after: avoid capture; use runtime construction\npublic void process(Class<?> type) {\n  TypeToken<?> token = TypeToken.get(type);\n}\n\n// build with javac instead of ecj, or update Eclipse JDT/JDK to a fixed release.","handlingStrategy":"fallback","validationCode":"// detect affected toolchain early; recommend build-system switch\nSystem.out.println(\"javac/ecj: \" + System.getProperty(\"java.vm.version\"));\n// prefer building with javac; in tests assert a known TypeToken resolves\nTypeToken<?> probe = TypeToken.get(String.class);\nif (probe == null) throw new IllegalStateException();","typeGuard":null,"tryCatchPattern":"try {\n  return new TypeToken<T>() {}; // local class generic method on buggy JVM\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"TypeToken captured `null`\")) {\n    // bypass verify by constructing at runtime\n    return TypeToken.get(elementType);\n  }\n  throw e;\n}","preventionTips":["Build with javac rather than Eclipse ecj in CI/release pipelines.","Keep JDK current; the underlying reflection bug is fixed in later point releases.","Avoid constructing TypeTokens inside local classes with generic methods.","Use TypeToken.get / getParameterized for runtime types."],"tags":["gson","type-token","generics","eclipse-jdt","compiler-bug","jvm","illegal-argument"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}