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
- For a 'super' wildcard, set the upper bound to Object.class and supply only the lower bound.
- Use Gson's Types.supertypeOf(lowerBound) which sets upperBound = Object.class automatically.
- 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
- For 'super' wildcards, always pair the lower bound with upperBound = Object.class.
- Use supertypeOf(...) which sets this correctly.
- Never set both a constrained upper bound and a lower bound on the same wildcard.
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
- At most one lower bound is supported
- Exactly one upper bound must be specified
- 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/a85063cdfa813b6e.json.
Report an issue: GitHub.