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 (GsonTypes) when a lower bound is present (a 'super' wildcard, e.g. ? super T) but the upper bound is not Object.class. Per Java semantics, a wildcard with a lower bound implicitly has Object as its upper bound; any other upper bound is contradictory (you cannot say ? super T extends Number). Gson enforces this so the normalized wildcard is well-formed.

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 8b8628c656)

Solutions

  1. For a 'super' wildcard, set the upper bound to Object.class and supply only the lower bound.
  2. Use Gson's Types.supertypeOf(lowerBound) which sets upperBound = Object.class automatically.
  3. If you genuinely need a constrained upper bound, remove the lower bound and use Types.subtypeOf(upperBound) instead.

Example fix

// before: contradictory bounds
new WildcardType() {
  public Type[] getUpperBounds() { return new Type[] { Number.class }; }
  public Type[] getLowerBounds() { return new Type[] { Integer.class }; }
};

// after: proper 'super' wildcard
Type t = com.google.gson.internal.$Gson$Types.supertypeOf(Integer.class);
Defensive patterns

Strategy: validation

Validate before calling

static boolean isConsistentWildcard(WildcardType w) {
  Type[] lb = w.getLowerBounds();
  Type[] ub = w.getUpperBounds();
  return lb.length == 0 || (ub.length == 1 && ub[0] == Object.class);
}

Type guard

null

Try / catch

try {
  canonicalize(wildcardType);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("upper bound must be Object")) {
    // rebuild as a pure 'super' wildcard
    wildcardType = com.google.gson.internal.$Gson$Types.supertypeOf(wildcardType.getLowerBounds()[0]);
  } else throw e;
}

Prevention

When it happens

Trigger: Building a wildcard type with both a non-null lower bound and an upper bound other than Object.class. Occurs when a custom WildcardType returns a lower bound from getLowerBounds() and a non-Object element from getUpperBounds(), or when an intersection-style type is mis-modeled as a single wildcard.

Common situations: Hand-constructed WildcardType implementations that mistakenly set both bounds; reflective interop with libraries that build wildcard types loosely; copy-paste errors when defining type tokens.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/a85063cdfa813b6e.json. Report an issue: GitHub.