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 when the upperBounds array does not have exactly one element. Java wildcards always have a well-defined upper bound (Object if none is written), so Gson requires exactly one upper bound. Zero or more than one is treated as malformed input (GsonTypes.java:629).
Solutions
- Always pass exactly one upper bound; default to Object.class when none is intended.
- If you need an intersection, collapse to the common supertype and document the limitation, or use a TypeVariable instead.
- Use TypeToken construction helpers instead of the internal WildcardTypeImpl directly.
Example fix
// before
new WildcardTypeImpl(new Type[0], new Type[0]); // throws: 0 upper bounds
// after
new WildcardTypeImpl(new Type[]{Object.class}, new Type[0]); Defensive patterns
Strategy: validation
Validate before calling
if (upperBounds == null || upperBounds.length != 1) throw new IllegalArgumentException("exactly one upper bound required"); Type guard
static boolean singleUpperBound(Type[] ubs) { return ubs != null && ubs.length == 1; } Prevention
- Always default upper bounds to Object.class when none is intended.
- Avoid building WildcardTypeImpl directly; use TypeToken/TypeUtils.
- Never copy getUpperBounds() arrays verbatim into a new wildcard without normalization.
When it happens
Trigger: Calling the WildcardTypeImpl constructor with an empty upperBounds array (forgetting Object.class) or with multiple upper bounds (the language does not support intersection-bounded wildcards at source level).
Common situations: Hand-rolled Type factories passing new Type[0] for upper bounds; code copied from getUpperBounds() that includes synthetic bounds; libraries bridging TypeMirror -> Type.
Related errors
- At most one lower bound is supported
- When lower bound is specified, upper bound must be Object
- Must specify owner type for
- Primitive type is not allowed
- Expected a Class, ParameterizedType, or GenericArrayType…
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/59048385c6e20ac5.
Report an issue: GitHub.
Appendix: 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 310ac341f2)