google/gson · error · IllegalArgumentException

At most one lower bound is supported

Error message

At most one lower bound is supported

What it means

Thrown by WildcardTypeImpl (GsonTypes) when more than one lower bound is supplied. Java wildcards (e.g. ? super T) allow only a single lower bound syntactically; the WildcardType interface permits an array but Gson deliberately supports at most one (see JDK-8250660). Constructing a wildcard with two lower bounds is rejected because it cannot be expressed in source.

Source

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

    public String toString() {
      return typeToString(componentType) + "[]";
    }
  }

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

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Ensure any custom WildcardType.getLowerBounds() returns at most one element; keep only the first bound.
  2. Avoid building wildcard types by hand; use Gson's Types.supertypeOf(singleBound) or Types.subtypeOf(singleBound).
  3. If the extra bounds are genuine, the type cannot be modeled as a single wildcard; split into separate types or widen to a non-wildcard parameterized type.

Example fix

// before: custom WildcardType with two lower bounds
public Type[] getLowerBounds() { return new Type[] { String.class, Number.class }; }

// after: single lower bound only
public Type[] getLowerBounds() { return new Type[] { String.class }; }
Defensive patterns

Strategy: validation

Validate before calling

static Type normalizeWildcard(WildcardType w) {
  Type[] lb = w.getLowerBounds();
  if (lb.length > 1) {
    // keep only the first lower bound
    return com.google.gson.internal.$Gson$Types.supertypeOf(lb[0]);
  }
  return w;
}

Type guard

static boolean hasAtMostOneLowerBound(WildcardType w) {
  return w.getLowerBounds().length <= 1;
}

Try / catch

try {
  canonicalize(wildcardType);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("lower bound")) {
    wildcardType = normalizeWildcard(wildcardType);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling a wildcard-construction path (subtypeOf/supertypeOf or internal canonicalize of a WildcardType) with lowerBounds.length > 1. Happens when a hand-built WildcardType reports multiple lower bounds, or a reflective Type from a non-standard JVM/library advertises two lower bounds.

Common situations: Custom WildcardType implementations returning arrays of length > 1 from getLowerBounds(); reflection on synthetic/bridge types produced by older JVMs or annotation processors; interop with libraries that build wildcard types permissively.

Related errors


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