google/gson · error · JsonParseException
cannot deserialize {baseType} because it does not define a f
Error message
cannot deserialize {baseType} because it does not define a field named {typeFieldName} What it means
Thrown during deserialization by RuntimeTypeAdapterFactory's read() when the incoming JSON object contains no field whose name matches the configured typeFieldName (default "type"). The adapter relies on that discriminator field to decide which registered subtype to instantiate; without it there is no way to pick the right class. This is a JsonParseException surfaced through gson.fromJson.
Source
Thrown at extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java:276
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
return new TypeAdapter<R>() {
@Override
public R read(JsonReader in) throws IOException {
JsonElement jsonElement = jsonElementAdapter.read(in);
JsonElement labelJsonElement;
if (maintainType) {
labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
} else {
labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
}
if (labelJsonElement == null) {
throw new JsonParseException(
"cannot deserialize "
+ baseType
+ " because it does not define a field named "
+ typeFieldName);
}
String label = labelJsonElement.getAsString();
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
if (delegate == null) {
throw new JsonParseException(
"cannot deserialize "
+ baseType
+ " subtype named "
+ label
+ "; did you forget to register a subtype?");
}
return delegate.fromJsonTree(jsonElement);
}View on GitHub (pinned to 8b8628c656)
Solutions
- Ensure the same RuntimeTypeAdapterFactory (same base type AND same typeFieldName) is registered on the Gson instance used for both serialization and deserialization.
- Verify the JSON actually contains the discriminator field, e.g. jsonObject.has("type"), before deserializing.
- If consuming external JSON, serialize a sample object first and inspect the output to confirm the exact field name and label values.
- If you must accept JSON without the discriminator, write a pre-processing step (JsonElement transform) that injects the field, or use a different deserialization strategy.
Example fix
// before: producer lacks the factory, so JSON has no "type" field
Gson producer = new Gson();
String json = producer.toJson(diamond, Shape.class);
Shape s = consumerGson.fromJson(json, Shape.class); // throws
// after: producer and consumer share the factory
RuntimeTypeAdapterFactory<Shape> f = RuntimeTypeAdapterFactory.of(Shape.class, "type")
.registerSubtype(Diamond.class);
Gson producer = new GsonBuilder().registerTypeAdapterFactory(f).create();
Gson consumer = new GsonBuilder().registerTypeAdapterFactory(f).create();
String json = producer.toJson(diamond, Shape.class);
Shape s = consumer.fromJson(json, Shape.class); Defensive patterns
Strategy: validation
Validate before calling
// Validate the JSON has the discriminator before deserializing
String typeFieldName = "type";
JsonElement root = JsonParser.parseString(json);
if (!root.isJsonObject() || !root.getAsJsonObject().has(typeFieldName)) {
throw new IllegalArgumentException("Missing discriminator field '" + typeFieldName + "'");
}
Shape s = gson.fromJson(root, Shape.class); Type guard
static boolean hasDiscriminator(String json, String typeFieldName) {
try {
JsonElement e = JsonParser.parseString(json);
return e.isJsonObject() && e.getAsJsonObject().has(typeFieldName);
} catch (JsonSyntaxException ex) { return false; }
} Try / catch
try {
Shape s = gson.fromJson(json, Shape.class);
} catch (JsonParseException e) {
if (e.getMessage().contains("does not define a field named")) {
// handle missing discriminator: log and reject, or retry with a default subtype
} else throw e;
} Prevention
- Share the exact same RuntimeTypeAdapterFactory (same base type + typeFieldName) across producer and consumer Gson instances.
- Serialize a sample object and assert the JSON contains the discriminator field as a contract test.
- When integrating with external JSON, document and validate the required discriminator field name and allowed labels.
- Use a JsonElement pre-check rather than relying on the exception for control flow.
When it happens
Trigger: Deserializing JSON that was serialized without the runtime type adapter (plain gson.toJson produced no "type" field); serializing with gson.toJson(obj, Shape.class) but then reading back through a Gson instance that lacks the RuntimeTypeAdapterFactory registered; the JSON producer renamed or omitted the discriminator field; maintainType=false and the field was stripped on the producer side.
Common situations: Producer and consumer Gson instances are configured differently (producer has no factory); an external system sends hand-built JSON without the type field; field name mismatch because the producer used of(Shape.class, "kind") and the consumer used of(Shape.class, "type"); legacy JSON predating the polymorphic adapter.
Related errors
- cannot deserialize {baseType} subtype named {label}; did you
- types and labels must be unique
- cannot serialize {srcType.getName()}; did you forget to regi
- cannot serialize {srcType.getName()} because it already defi
- Missing {fieldName} field; at path {path}
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/2c119bda15ec6227.json.
Report an issue: GitHub.