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 (GsonTypes) when the number of upper bounds is not exactly one. A Java wildcard always has an implicit upper bound (Object when unspecified), so Gson normalizes every wildcard to exactly one upper bound. Zero upper bounds or more than one upper bound (other than the single-bound case) is rejected because the target Java version supports at most one bound per wildcard.
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 8b8628c656)
Solutions
- Ensure the WildcardType reports exactly one upper bound; if multiple intersection bounds exist, collapse to their common supertype or use Object.class.
- Use Types.subtypeOf(bound) so Gson sets upperBound = bound and handles normalization for you.
- Avoid constructing wildcard types manually; let the compiler/Gson derive them.
Example fix
// before: zero upper bounds
public Type[] getUpperBounds() { return new Type[0]; }
// after: exactly one
public Type[] getUpperBounds() { return new Type[] { Number.class }; } Defensive patterns
Strategy: validation
Validate before calling
static Type normalizeUpperBounds(WildcardType w) {
Type[] ub = w.getUpperBounds();
if (ub.length != 1) {
Type bound = ub.length == 0 ? Object.class : ub[0];
return com.google.gson.internal.$Gson$Types.subtypeOf(bound);
}
return w;
} Type guard
static boolean hasExactlyOneUpperBound(WildcardType w) {
return w.getUpperBounds().length == 1;
} Try / catch
try {
canonicalize(wildcardType);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("upper bound")) {
wildcardType = normalizeUpperBounds(wildcardType);
} else throw e;
} Prevention
- Always return exactly one upper bound from custom WildcardType.getUpperBounds().
- Use Object.class as the implicit upper bound when none is specified.
- Prefer subtypeOf(bound) over manual construction.
When it happens
Trigger: Constructing a wildcard type whose getUpperBounds() returns an empty array or an array with length >= 2. Reached via internal canonicalization of a hand-built or reflectively-obtained WildcardType that does not expose exactly one upper bound.
Common situations: Custom WildcardType implementations returning no upper bounds or multiple intersection upper bounds; reflective types produced by annotation processors modeling intersection types; misuse of Types helper methods.
Related errors
- At most one lower bound is supported
- When lower bound is specified, upper bound must be Object
- Primitive type is not allowed
- Must specify owner type for {rawType}
- TypeToken type argument must not contain a type variable; ca
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/59048385c6e20ac5.json.
Report an issue: GitHub.