{"record":{"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/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/GsonTypes.java#L612-L648","documentation":"Thrown by WildcardTypeImpl when the upperBounds array does not have exactly one element. Java wildcards always have a well-defined upper bound (Object if none is written), so Gson requires exactly one upper bound. Zero or more than one is treated as malformed input (GsonTypes.java:629).","triggerScenarios":"Calling the WildcardTypeImpl constructor with an empty upperBounds array (forgetting Object.class) or with multiple upper bounds (the language does not support intersection-bounded wildcards at source level).","commonSituations":"Hand-rolled Type factories passing new Type[0] for upper bounds; code copied from getUpperBounds() that includes synthetic bounds; libraries bridging TypeMirror -> Type.","solutions":["Always pass exactly one upper bound; default to Object.class when none is intended.","If you need an intersection, collapse to the common supertype and document the limitation, or use a TypeVariable instead.","Use TypeToken construction helpers instead of the internal WildcardTypeImpl directly."],"exampleFix":"// before\nnew WildcardTypeImpl(new Type[0], new Type[0]); // throws: 0 upper bounds\n// after\nnew WildcardTypeImpl(new Type[]{Object.class}, new Type[0]);","handlingStrategy":"validation","validationCode":"if (upperBounds == null || upperBounds.length != 1) throw new IllegalArgumentException(\"exactly one upper bound required\");","typeGuard":"static boolean singleUpperBound(Type[] ubs) { return ubs != null && ubs.length == 1; }","tryCatchPattern":null,"preventionTips":["Always default upper bounds to Object.class when none is intended.","Avoid building WildcardTypeImpl directly; use TypeToken/TypeUtils.","Never copy getUpperBounds() arrays verbatim into a new wildcard without normalization."],"tags":["gson","reflection","generics","wildcard"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}