google/gson · error · JsonSyntaxException
Expected a " + requestedType.getName() + " but was " + resul
Error message
Expected a " + requestedType.getName() + " but was " + result.getClass().getName() + "; at path " + in.getPreviousPath()
What it means
Gson's newTypeHierarchyFactory wraps an adapter so that, after reading, it verifies the result is an instance of the requested type. If the delegate produces a different concrete type, Gson throws JsonSyntaxException 'Expected a <X> but was <Y>'. This catches mismatches in type-hierarchy adapters (e.g. Number, InetAddress, Collection).
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java:1081
@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 8b8628c656)
Solutions
- Ensure the registered TypeAdapter returns an instance assignable to the requested type.
- Register an InstanceCreator or factory for the specific concrete subtype you expect.
- Use a concrete target type (e.g. ArrayList instead of Collection) when deserializing.
- If you wrote a custom hierarchy adapter, add a runtime check and construct the correct concrete type.
Example fix
// before: adapter for Number returns Double even when Long is requested
Gson g = new GsonBuilder().registerTypeHierarchyAdapter(Number.class, (JsonSerializer<Number>)(s, t, c) -> new JsonPrimitive(s.doubleValue())).create();
// after: register a factory that honors the requested type
Gson g = new GsonBuilder().registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!Number.class.isAssignableFrom(type.getRawType())) return null;
return (TypeAdapter<T>) new TypeAdapter<Number>() {
@Override public Number read(JsonReader in) throws IOException { return in.nextLong(); }
@Override public void write(JsonWriter out, Number v) throws IOException { out.value(v); }
};
}
}).create(); Defensive patterns
Strategy: try-catch
Validate before calling
boolean adapterReturnsAssignable(TypeAdapterFactory f, Gson g, TypeToken<?> requested) {
// smoke-test: build adapter and parse a sample; verify instance type
return true; // real validation is integration testing the factory
} Type guard
static boolean isAssignableTo(Class<?> requested, Object result) {
return result == null || requested.isInstance(result);
} Try / catch
try {
T result = gson.fromJson(json, type);
} catch (JsonSyntaxException e) {
if (e.getMessage().contains("Expected a")) { /* register correct factory or use concrete type */ }
else throw e;
} Prevention
- Use concrete target types (ArrayList, HashSet) instead of interfaces where possible.
- Unit-test custom hierarchy factories with all expected subtypes.
- Ensure custom adapters return instances assignable to the requested raw type.
When it happens
Trigger: Deserializing a type that uses a hierarchy factory (such as Collection, Number, InetAddress subclasses) where the underlying adapter returns a concrete type that is not assignable to the declared target type, e.g. asking for Set but the factory only ever produces List.
Common situations: Registering a custom adapter for a supertype that returns an incompatible subtype, declaring an interface/abstract type whose only available implementation is not assignable, or a JSON shape that makes the default adapter choose a different concrete class.
Related errors
- Expecting number, got: " + jsonToken + "; at path " + in.get
- duplicate key: {key}
- Unexpected token: " + peeked
- Failed parsing '" + s + "' as BigDecimal; at path " + in.get
- Failed parsing '" + s + "' as BigInteger; at path " + in.get
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/aae138136ccb1284.json.
Report an issue: GitHub.