google/gson · error · IllegalArgumentException

Exactly one upper bound must be specified

Error message

Exactly one upper bound must be specified

What it means

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).

Solutions

  1. Always pass exactly one upper bound; default to Object.class when none is intended.
  2. If you need an intersection, collapse to the common supertype and document the limitation, or use a TypeVariable instead.
  3. Use TypeToken construction helpers instead of the internal WildcardTypeImpl directly.

Example fix

// before
new WildcardTypeImpl(new Type[0], new Type[0]); // throws: 0 upper bounds
// after
new WildcardTypeImpl(new Type[]{Object.class}, new Type[0]);
Defensive patterns

Strategy: validation

Validate before calling

if (upperBounds == null || upperBounds.length != 1) throw new IllegalArgumentException("exactly one upper bound required");

Type guard

static boolean singleUpperBound(Type[] ubs) { return ubs != null && ubs.length == 1; }

Prevention

When it happens

Trigger: 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).

Common situations: Hand-rolled Type factories passing new Type[0] for upper bounds; code copied from getUpperBounds() that includes synthetic bounds; libraries bridging TypeMirror -> Type.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/59048385c6e20ac5. Report an issue: GitHub.

Appendix: source

Thrown at gson/src/main/java/com/google/gson/internal/GsonTypes.java:630

  }

  /**
   * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
   * support what the target Java version supports - at most one bound, see also
   * https://bugs.openjdk.java.net/browse/JDK-8250660. If a lower bound is set, the upper bound must
   * be Object.class.
   */
  private static final class WildcardTypeImpl implements WildcardType {
    private final Type upperBound;

    private final Type lowerBound;

    WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
      if (lowerBounds.length > 1) {
        throw new IllegalArgumentException("At most one lower bound is supported");
      }
      if (upperBounds.length != 1) {
        throw new IllegalArgumentException("Exactly one upper bound must be specified");
      }

      if (lowerBounds.length == 1) {
        requireNonNull(lowerBounds[0]);
        checkNotPrimitive(lowerBounds[0]);
        if (upperBounds[0] != Object.class) {
          throw new IllegalArgumentException(
              "When lower bound is specified, upper bound must be Object");
        }
        this.lowerBound = canonicalize(lowerBounds[0]);
        this.upperBound = Object.class;

      } else {
        requireNonNull(upperBounds[0]);
        checkNotPrimitive(upperBounds[0]);
        this.lowerBound = null;
        this.upperBound = canonicalize(upperBounds[0]);
      }

View on GitHub (pinned to 310ac341f2)