google/gson · error · JsonSyntaxException

Expected a ${requestedTypeName} but was ${resultClassName};

Error message

Expected a ${requestedTypeName} but was ${resultClassName}; at path ${path}

What it means

Thrown by newTypeHierarchyFactory (used by adapters like INET_ADDRESS_FACTORY) after the wrapped TypeAdapter.read() returns a value. It performs a runtime instanceof check: if the produced object is not an instance of the requestedType (the concrete subtype requested via TypeToken), it throws JsonSyntaxException. This guards against an adapter producing a broader type than the field declared.

Source

Thrown at gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java:1074

      @SuppressWarnings("unchecked")
      @Override
      public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) {
        Class<? super T2> requestedType = typeToken.getRawType();
        if (!clazz.isAssignableFrom(requestedType)) {
          return null;
        }
        return (TypeAdapter<T2>)
            new TypeAdapter<T1>() {
              @Override
              public void write(JsonWriter out, T1 value) throws IOException {
                typeAdapter.write(out, value);
              }

              @Override
              public T1 read(JsonReader in) throws IOException {
                T1 result = typeAdapter.read(in);
                if (result != null && !requestedType.isInstance(result)) {
                  throw new JsonSyntaxException(
                      "Expected a "
                          + requestedType.getName()
                          + " but was "
                          + result.getClass().getName()
                          + "; at path "
                          + in.getPreviousPath());
                }
                return result;
              }
            };
      }

      @Override
      public String toString() {
        return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
      }
    };
  }

View on GitHub (pinned to 310ac341f2)

Solutions

  1. Change the field type to the base type the adapter actually produces (e.g. InetAddress instead of Inet4Address).
  2. Register a custom TypeAdapter for the exact subtype that parses and validates the address family before returning.
  3. Pre-validate the address family (e.g. regex or InetAddress.getByName then instanceof) and reject/normalize at the boundary.
  4. Avoid declaring fields as narrow reflection-only subtypes when the data cannot guarantee the family.

Example fix

// before
public class Conn { public Inet4Address peer; } // may resolve as Inet6Address -> fails
// JSON: {"peer":"::1"}

// after: use the base type the adapter produces
public class Conn { public InetAddress peer; }
// or validate explicitly:
InetAddress a = InetAddress.getByName(s);
if (!(a instanceof Inet4Address)) throw new IllegalArgumentException("not v4: "+s);
Defensive patterns

Strategy: type-guard

Validate before calling

// pre-validate address family before deserialization if you know the value
String raw = jsonNode.get("peer").getAsString();
InetAddress a = InetAddress.getByName(raw);
if (!(a instanceof Inet4Address)) {
  throw new IllegalArgumentException("Not IPv4: " + raw);
}

Type guard

public static boolean isInet4Address(JsonElement e) {
  try {
    return InetAddress.getByName(e.getAsString()) instanceof Inet4Address;
  } catch (Exception ex) { return false; }
}

Try / catch

try {
  return gson.fromJson(json, Conn.class);
} catch (JsonSyntaxException e) {
  if (e.getMessage().startsWith("Expected a ")) {
    // narrow field type to InetAddress, or reject record
    throw new IllegalArgumentException("Address family mismatch", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: A field is declared as a subtype that the hierarchy adapter cannot guarantee, e.g. java.net.Inet4Address or Inet6Address but the JSON value resolves to the other family; or a custom hierarchy-registered adapter returning a base type. Concretely, INET_ADDRESS returns an InetAddress (which is concrete) so requesting Inet4Address/Inet6Address directly triggers this when the resolved address is of the wrong family.

Common situations: Modeling IPv4-vs-IPv6 as Inet4Address/Inet6Address fields; declaring Collection subtypes (e.g. a field of type a specific List impl) when Gson's adapter returns the generic; custom factories registered through newTypeHierarchyFactory returning superclass instances.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/65dda026005f2ec7. Report an issue: GitHub.