google/gson · error · JsonParseException
cannot serialize ${srcType.getName()} because it already def
Error message
cannot serialize ${srcType.getName()} because it already defines a field named ${typeFieldName} What it means
Thrown during serialization by RuntimeTypeAdapterFactory (when maintainType is false) if the subtype being serialized already serializes a field whose name collides with the configured typeFieldName. Because the factory prepends its own discriminator field with that exact name, a collision would overwrite or shadow the real field, so it aborts with a JsonParseException. It protects data integrity of the discriminator versus a model field.
Source
Thrown at extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java:316
Class<?> srcType = value.getClass();
String label = subtypeToLabel.get(srcType);
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
if (delegate == null) {
throw new JsonParseException(
"cannot serialize " + srcType.getName() + "; did you forget to register a subtype?");
}
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
if (maintainType) {
jsonElementAdapter.write(out, jsonObject);
return;
}
JsonObject clone = new JsonObject();
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException(
"cannot serialize "
+ srcType.getName()
+ " because it already defines a field named "
+ typeFieldName);
}
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
jsonElementAdapter.write(out, clone);
}
}.nullSafe();
}
}
View on GitHub (pinned to 310ac341f2)
Solutions
- Pick a typeFieldName that does not collide with any serialized field, e.g. RuntimeTypeAdapterFactory.of(Base.class, "__type").
- Rename or @SerializedName the conflicting domain field to a non-colliding key.
- If you must keep the same name, use of(Base.class, typeFieldName, true) (maintainType=true) so the factory does not inject/clone the discriminator.
Example fix
// before
class Event { String type; } // collides with default discriminator
RuntimeTypeAdapterFactory.of(Event.class); // "type"
// after
RuntimeTypeAdapterFactory.of(Event.class, "@type"); Defensive patterns
Strategy: validation
Validate before calling
// Detect field-name collision with the discriminator up front
String typeFieldName = "type";
for (Field f : subtypeClass.getDeclaredFields()) {
String jsonName = f.isAnnotationPresent(SerializedName.class)
? f.getAnnotation(SerializedName.class).value() : f.getName();
if (jsonName.equals(typeFieldName)) {
throw new IllegalStateException("Field '" + jsonName + "' collides with discriminator");
}
} Try / catch
try { gson.toJson(obj, Shape.class); }
catch (JsonParseException e) {
if (e.getMessage().contains("already defines a field named")) {
// switch typeFieldName or rename the field
} else throw e;
} Prevention
- Choose a discriminator name unlikely to collide (e.g. "@type").
- Run a smoke serialization test for each registered subtype.
- If the field name must match, use maintainType=true.
When it happens
Trigger: The subtype class declares a field literally named "type" (the default discriminator); typeFieldName was customized to a name that the model already uses (e.g. "kind", "id"); a parent class introduces a field that collides after the factory was configured.
Common situations: Default typeFieldName "type" clashes with a domain field; renaming the discriminator to something already used elsewhere in the model; inheriting a base class that already has the conflicting field.
Related errors
- cannot serialize ${srcType.getName()}; did you forget to reg
- types and labels must be unique
- cannot deserialize ${baseType} because it does not define a
- cannot deserialize ${baseType} subtype named ${label}; did y
- Deserialization is unsupported
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/08ee0d763321b724.
Report an issue: GitHub.