{"id":"59048385c6e20ac5","repo":"google/gson","slug":"exactly-one-upper-bound-must-be-specified","errorCode":null,"errorMessage":"Exactly one upper bound must be specified","messagePattern":"Exactly one upper bound must be specified","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/GsonTypes.java","lineNumber":630,"sourceCode":"  }\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]);\n        this.lowerBound = null;\n        this.upperBound = canonicalize(upperBounds[0]);\n      }","sourceCodeStart":612,"sourceCodeEnd":648,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/GsonTypes.java#L612-L648","documentation":"Thrown by WildcardTypeImpl (GsonTypes) when the number of upper bounds is not exactly one. A Java wildcard always has an implicit upper bound (Object when unspecified), so Gson normalizes every wildcard to exactly one upper bound. Zero upper bounds or more than one upper bound (other than the single-bound case) is rejected because the target Java version supports at most one bound per wildcard.","triggerScenarios":"Constructing a wildcard type whose getUpperBounds() returns an empty array or an array with length >= 2. Reached via internal canonicalization of a hand-built or reflectively-obtained WildcardType that does not expose exactly one upper bound.","commonSituations":"Custom WildcardType implementations returning no upper bounds or multiple intersection upper bounds; reflective types produced by annotation processors modeling intersection types; misuse of Types helper methods.","solutions":["Ensure the WildcardType reports exactly one upper bound; if multiple intersection bounds exist, collapse to their common supertype or use Object.class.","Use Types.subtypeOf(bound) so Gson sets upperBound = bound and handles normalization for you.","Avoid constructing wildcard types manually; let the compiler/Gson derive them."],"exampleFix":"// before: zero upper bounds\npublic Type[] getUpperBounds() { return new Type[0]; }\n\n// after: exactly one\npublic Type[] getUpperBounds() { return new Type[] { Number.class }; }","handlingStrategy":"validation","validationCode":"static Type normalizeUpperBounds(WildcardType w) {\n  Type[] ub = w.getUpperBounds();\n  if (ub.length != 1) {\n    Type bound = ub.length == 0 ? Object.class : ub[0];\n    return com.google.gson.internal.$Gson$Types.subtypeOf(bound);\n  }\n  return w;\n}","typeGuard":"static boolean hasExactlyOneUpperBound(WildcardType w) {\n  return w.getUpperBounds().length == 1;\n}","tryCatchPattern":"try {\n  canonicalize(wildcardType);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"upper bound\")) {\n    wildcardType = normalizeUpperBounds(wildcardType);\n  } else throw e;\n}","preventionTips":["Always return exactly one upper bound from custom WildcardType.getUpperBounds().","Use Object.class as the implicit upper bound when none is specified.","Prefer subtypeOf(bound) over manual construction."],"tags":["gson","generics","reflection","wildcard","type-bound"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}