{"id":"481e0800b6fd9974","repo":"google/gson","slug":"at-most-one-lower-bound-is-supported","errorCode":null,"errorMessage":"At most one lower bound is supported","messagePattern":"At most one lower bound is supported","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/GsonTypes.java","lineNumber":627,"sourceCode":"    public String toString() {\n      return typeToString(componentType) + \"[]\";\n    }\n  }\n\n  /**\n   * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only\n   * support what the target Java version supports - at most one bound, see also\n   * https://bugs.openjdk.java.net/browse/JDK-8250660. If a lower bound is set, the upper bound must\n   * be Object.class.\n   */\n  private static final class WildcardTypeImpl implements WildcardType {\n    private final Type upperBound;\n\n    private final Type lowerBound;\n\n    WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {\n      if (lowerBounds.length > 1) {\n        throw new IllegalArgumentException(\"At most one lower bound is supported\");\n      }\n      if (upperBounds.length != 1) {\n        throw new IllegalArgumentException(\"Exactly one upper bound must be specified\");\n      }\n\n      if (lowerBounds.length == 1) {\n        requireNonNull(lowerBounds[0]);\n        checkNotPrimitive(lowerBounds[0]);\n        if (upperBounds[0] != Object.class) {\n          throw new IllegalArgumentException(\n              \"When lower bound is specified, upper bound must be Object\");\n        }\n        this.lowerBound = canonicalize(lowerBounds[0]);\n        this.upperBound = Object.class;\n\n      } else {\n        requireNonNull(upperBounds[0]);\n        checkNotPrimitive(upperBounds[0]);","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/GsonTypes.java#L609-L645","documentation":"Thrown by WildcardTypeImpl (GsonTypes) when more than one lower bound is supplied. Java wildcards (e.g. ? super T) allow only a single lower bound syntactically; the WildcardType interface permits an array but Gson deliberately supports at most one (see JDK-8250660). Constructing a wildcard with two lower bounds is rejected because it cannot be expressed in source.","triggerScenarios":"Calling a wildcard-construction path (subtypeOf/supertypeOf or internal canonicalize of a WildcardType) with lowerBounds.length > 1. Happens when a hand-built WildcardType reports multiple lower bounds, or a reflective Type from a non-standard JVM/library advertises two lower bounds.","commonSituations":"Custom WildcardType implementations returning arrays of length > 1 from getLowerBounds(); reflection on synthetic/bridge types produced by older JVMs or annotation processors; interop with libraries that build wildcard types permissively.","solutions":["Ensure any custom WildcardType.getLowerBounds() returns at most one element; keep only the first bound.","Avoid building wildcard types by hand; use Gson's Types.supertypeOf(singleBound) or Types.subtypeOf(singleBound).","If the extra bounds are genuine, the type cannot be modeled as a single wildcard; split into separate types or widen to a non-wildcard parameterized type."],"exampleFix":"// before: custom WildcardType with two lower bounds\npublic Type[] getLowerBounds() { return new Type[] { String.class, Number.class }; }\n\n// after: single lower bound only\npublic Type[] getLowerBounds() { return new Type[] { String.class }; }","handlingStrategy":"validation","validationCode":"static Type normalizeWildcard(WildcardType w) {\n  Type[] lb = w.getLowerBounds();\n  if (lb.length > 1) {\n    // keep only the first lower bound\n    return com.google.gson.internal.$Gson$Types.supertypeOf(lb[0]);\n  }\n  return w;\n}","typeGuard":"static boolean hasAtMostOneLowerBound(WildcardType w) {\n  return w.getLowerBounds().length <= 1;\n}","tryCatchPattern":"try {\n  canonicalize(wildcardType);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"lower bound\")) {\n    wildcardType = normalizeWildcard(wildcardType);\n  } else throw e;\n}","preventionTips":["Avoid hand-building WildcardType; use Gson's supertypeOf/subtypeOf helpers.","If you must, guarantee getLowerBounds().length <= 1.","Unit-test custom Type implementations against Gson's canonicalizer."],"tags":["gson","generics","reflection","wildcard","type-bound"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}