{"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/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/reflect/TypeToken.java#L137-L173","documentation":"Thrown by verifyNoTypeVariable when the captured type argument is unexpectedly null. This is a defensive check for a known compiler/runtime bug (not a user mistake): the Eclipse JDT compiler and certain JDK builds (e.g. Java 11.0.18) return null for a type variable declared by a method of a local class. Gson surfaces it rather than silently producing a broken TypeToken.","triggerScenarios":"Compiling with Eclipse JDT (ecj) and creating a TypeToken whose argument is a type variable declared by a method of a local (non-member) class. The reflective generic superclass returns null for the actual type argument, so verifyNoTypeVariable hits the `type == null` branch at TypeToken.java:155.","commonSituations":"Developers using Eclipse IDE or building with ecj who construct TypeTokens referencing method-local type variables; upgrading the JDK to a patched 11.0.x that changed type-variable reflection behavior; rarely reproduced under javac.","solutions":["Refactor to avoid capturing a type variable in the TypeToken; pass an explicit Class/Type to TypeToken.getParameterized instead.","Switch the compiler from Eclipse ecj to javac for the affected module, or upgrade Eclipse JDT to a version with the fix (see eclipse-jdt/eclipse.jdt.core#975).","Upgrade the JDK to a build that does not exhibit the null type-argument regression.","As a last resort, set gson.allowCapturingTypeVariables=true which skips verifyNoTypeVariable entirely (note this disables the related type-variable guard too)."],"exampleFix":"// before (Eclipse ecj returns null for the type variable)\n<T> void handle() {\n  class Local {\n    TypeToken<T> token = new TypeToken<T>() {}; // captures null -> throws\n  }\n}\n\n// after\n<T> void handle(Class<T> typeClass) {\n  TypeToken<T> token = TypeToken.get(typeClass); // explicit, compiler-independent\n}","handlingStrategy":"validation","validationCode":"// Detect the compiler/runtime null-type-argument condition before constructing the token\nType superclass = myClass.getGenericSuperclass();\nif (superclass instanceof ParameterizedType) {\n  Type arg = ((ParameterizedType) superclass).getActualTypeArguments()[0];\n  if (arg == null) {\n    throw new IllegalStateException(\"Compiler returned null type argument; rebuild with javac or upgrade JDT\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  TypeToken<T> token = new TypeToken<T>() {};\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"captured `null`\")) {\n    // fall back to an explicit Class-based token\n    token = TypeToken.get(fallbackClass);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Avoid constructing TypeTokens that reference method-local type variables, especially under Eclipse ecj.","Build with javac in CI to catch ecj-specific reflection quirks.","Keep Eclipse JDT and JDK versions current to avoid the regression (eclipse.jdt.core#975).","Pass explicit Class<T> arguments to bypass reflective capture entirely."],"tags":["gson","typetoken","compiler-bug","eclipse-jdt","reflection"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}