google/gson · error · IllegalArgumentException

When lower bound is specified, upper bound must be Object

Error message

When lower bound is specified, upper bound must be Object

What it means

Thrown by WildcardTypeImpl when a lower bound is supplied but the upper bound is not exactly Object.class. A wildcard is either upper-bounded (<? extends T>, upper != Object, lower == none) or lower-bounded (<? super T>, upper == Object, lower == T); mixing both is meaningless in Java source. Gson enforces the invariant at GsonTypes.java:636.

Solutions

  1. Decide which form you mean: upper-bounded (only upperBounds set, lower empty) or lower-bounded (upper = Object.class, lower set).
  2. Drop the lower bound if you really want an upper-bounded wildcard, or set upper to Object.class if you really want a lower-bounded wildcard.
  3. Do not synthesize bounds from arbitrary type metadata without normalizing to one of the two legal wildcard shapes.

Example fix

// before (ambiguous; throws)
new WildcardTypeImpl(new Type[]{Number.class}, new Type[]{String.class});
// after (lower-bounded)
new WildcardTypeImpl(new Type[]{Object.class}, new Type[]{String.class});
Defensive patterns

Strategy: validation

Validate before calling

boolean validWildcard(Type[] up, Type[] lo) {
  if (lo != null && lo.length == 1) {
    return up != null && up.length == 1 && up[0] == Object.class;
  }
  return true;
}

Type guard

static boolean isLegalWildcard(Type[] up, Type[] lo) {
  return (lo == null || lo.length == 0) || (up != null && up.length == 1 && up[0] == Object.class);
}

Prevention

When it happens

Trigger: Constructing a WildcardTypeImpl with both a non-Object upper bound and a lower bound, e.g. new WildcardTypeImpl(new Type[]{Number.class}, new Type[]{String.class}).

Common situations: Programmatic Type builders that fill both arrays from a generic descriptor; bridging code that mirrors annotation/type-use metadata containing both bounds; malformed test fixtures.

Related errors


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

Appendix: source

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

   */
  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]);
      }
    }

    @Override
    public Type[] getUpperBounds() {
      return new Type[] {upperBound};
    }

View on GitHub (pinned to 310ac341f2)