{"id":"a85063cdfa813b6e","repo":"google/gson","slug":"when-lower-bound-is-specified-upper-bound-must-be","errorCode":null,"errorMessage":"When lower bound is specified, upper bound must be Object","messagePattern":"When lower bound is specified, upper bound must be Object","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/GsonTypes.java","lineNumber":637,"sourceCode":"   */\n  private static final class WildcardTypeImpl implements WildcardType {\n    private final Type upperBound;\n\n    private final Type lowerBound;\n\n    WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {\n      if (lowerBounds.length > 1) {\n        throw new IllegalArgumentException(\"At most one lower bound is supported\");\n      }\n      if (upperBounds.length != 1) {\n        throw new IllegalArgumentException(\"Exactly one upper bound must be specified\");\n      }\n\n      if (lowerBounds.length == 1) {\n        requireNonNull(lowerBounds[0]);\n        checkNotPrimitive(lowerBounds[0]);\n        if (upperBounds[0] != Object.class) {\n          throw new IllegalArgumentException(\n              \"When lower bound is specified, upper bound must be Object\");\n        }\n        this.lowerBound = canonicalize(lowerBounds[0]);\n        this.upperBound = Object.class;\n\n      } else {\n        requireNonNull(upperBounds[0]);\n        checkNotPrimitive(upperBounds[0]);\n        this.lowerBound = null;\n        this.upperBound = canonicalize(upperBounds[0]);\n      }\n    }\n\n    @Override\n    public Type[] getUpperBounds() {\n      return new Type[] {upperBound};\n    }\n","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/GsonTypes.java#L619-L655","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: contradictory bounds\nnew WildcardType() {\n  public Type[] getUpperBounds() { return new Type[] { Number.class }; }\n  public Type[] getLowerBounds() { return new Type[] { Integer.class }; }\n};\n\n// after: proper 'super' wildcard\nType t = com.google.gson.internal.$Gson$Types.supertypeOf(Integer.class);","handlingStrategy":"validation","validationCode":"static boolean isConsistentWildcard(WildcardType w) {\n  Type[] lb = w.getLowerBounds();\n  Type[] ub = w.getUpperBounds();\n  return lb.length == 0 || (ub.length == 1 && ub[0] == Object.class);\n}","typeGuard":"null","tryCatchPattern":"try {\n  canonicalize(wildcardType);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"upper bound must be Object\")) {\n    // rebuild as a pure 'super' wildcard\n    wildcardType = com.google.gson.internal.$Gson$Types.supertypeOf(wildcardType.getLowerBounds()[0]);\n  } else throw e;\n}","preventionTips":["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."],"tags":["gson","generics","reflection","wildcard","type-bound"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}