google/gson · error · IllegalArgumentException
Type argument {} does not satisfy bounds for type variable {
Error message
Type argument {} does not satisfy bounds for type variable {} declared by {} What it means
Thrown by TypeToken.getParameterized during the per-argument bounds loop (TypeToken.java:422-441). For each type argument it checks every declared bound of the corresponding type variable via rawBound.isAssignableFrom(rawTypeArgument); if the argument is not assignable to the bound, the type is rejected. This prevents constructing a generic type that the Java compiler itself would refuse, e.g. List<String> for a class declared <T extends Number>.
Source
Thrown at gson/src/main/java/com/google/gson/reflect/TypeToken.java:432
// Check for this here to avoid misleading exception thrown by ParameterizedTypeImpl
if (GsonTypes.requiresOwnerType(rawType)) {
throw new IllegalArgumentException(
"Raw type "
+ rawClass.getName()
+ " is not supported because it requires specifying an owner type");
}
for (int i = 0; i < expectedArgsCount; i++) {
Type typeArgument =
Objects.requireNonNull(typeArguments[i], "Type argument must not be null");
Class<?> rawTypeArgument = GsonTypes.getRawType(typeArgument);
TypeVariable<?> typeVariable = typeVariables[i];
for (Type bound : typeVariable.getBounds()) {
Class<?> rawBound = GsonTypes.getRawType(bound);
if (!rawBound.isAssignableFrom(rawTypeArgument)) {
throw new IllegalArgumentException(
"Type argument "
+ typeArgument
+ " does not satisfy bounds for type variable "
+ typeVariable
+ " declared by "
+ rawType);
}
}
}
return new TypeToken<>(GsonTypes.newParameterizedTypeWithOwner(null, rawClass, typeArguments));
}
/**
* Gets type literal for the array type whose elements are all instances of {@code componentType}.
*/
public static TypeToken<?> getArray(Type componentType) {
return new TypeToken<>(GsonTypes.arrayOf(componentType));View on GitHub (pinned to 310ac341f2)
Solutions
- Pass a type argument whose raw class is assignable to the declared bound (e.g. Integer or Double for <T extends Number>).
- Validate with boundClass.isAssignableFrom(argClass) before calling getParameterized.
- If the bound is genuinely too restrictive, relax or redesign the generic declaration.
- Surface a domain-specific error to the caller rather than letting the raw IllegalArgumentException escape.
Example fix
// before
class NumberList<T extends Number> {}
TypeToken<?> t = TypeToken.getParameterized(NumberList.class, String.class);
// after
class NumberList<T extends Number> {}
TypeToken<?> t = TypeToken.getParameterized(NumberList.class, Integer.class); Defensive patterns
Strategy: validation
Validate before calling
Class<?> raw = NumberList.class;
TypeVariable<?>[] vars = raw.getTypeParameters();
Type[] args = { Integer.class };
for (int i = 0; i < args.length; i++) {
Class<?> argRaw = GsonTypes.getRawType(args[i]);
for (Type b : vars[i].getBounds()) {
if (!((Class<?>) GsonTypes.getRawType(b)).isAssignableFrom(argRaw)) {
throw new IllegalArgumentException(args[i] + " violates bound " + b);
}
}
}
TypeToken<?> t = TypeToken.getParameterized(raw, args); Type guard
static boolean satisfiesBounds(Class<?> raw, int i, Type arg) {
Class<?> argRaw = GsonTypes.getRawType(arg);
for (Type b : raw.getTypeParameters()[i].getBounds()) {
if (!((Class<?>) GsonTypes.getRawType(b)).isAssignableFrom(argRaw)) return false;
}
return true;
} Try / catch
try {
return TypeToken.getParameterized(raw, args);
} catch (IllegalArgumentException e) {
throw new DomainTypeException("Invalid generic binding for " + raw, e);
} Prevention
- Validate assignability to declared bounds before constructing the token.
- Restrict user-supplied class inputs to an allowlist of permitted types.
- Unit-test boundary cases for each bounded generic you expose.
When it happens
Trigger: Passing String as the type argument for a variable bounded as <T extends Number>, e.g. getParameterized(NumberList.class, String.class) where NumberList<T extends Number>. Also triggered by bounded wildcards or custom interfaces where the argument class does not implement the required bound.
Common situations: Building TypeTokens dynamically from user-supplied class names without validating the bound; generic repository/helper APIs that accept a Class<?> and pass it straight to getParameterized; migrating from raw types to bounded generics and forgetting the constraint.
Related errors
- {} requires {} type arguments, but got {}
- Raw type {} is not supported because it requires specifying
- Primitive type is not allowed
- Must specify owner type for ${rawType}
- At most one lower bound is supported
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/0fd0e8798892d999.
Report an issue: GitHub.