google/gson · error · IllegalArgumentException
Class " + declaringType.getName() + " declares multiple JSON
Error message
Class " + declaringType.getName() + " declares multiple JSON fields named '" + duplicateName + "'; conflict is caused by fields " + ReflectionHelper.fieldToString(field1) + " and " + ReflectionHelper.fieldToString(field2) + "\nSee " + TroubleshootingGuide.createUrl("duplicate-fields") What it means
Thrown when two fields of the same class (or its inheritance chain) resolve to the same JSON name after applying the FieldNamingStrategy and @SerializedName, so Gson cannot decide which to use. It is an IllegalArgumentException raised eagerly during adapter construction (getBoundFields), with a link to the troubleshooting guide.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java:307
}
private static class FieldsData {
static final FieldsData EMPTY = new FieldsData(Collections.emptyMap(), Collections.emptyList());
/** Maps from JSON member name to field */
final Map<String, BoundField> deserializedFields;
final List<BoundField> serializedFields;
FieldsData(Map<String, BoundField> deserializedFields, List<BoundField> serializedFields) {
this.deserializedFields = deserializedFields;
this.serializedFields = serializedFields;
}
}
private static IllegalArgumentException createDuplicateFieldException(
Class<?> declaringType, String duplicateName, Field field1, Field field2) {
throw new IllegalArgumentException(
"Class "
+ declaringType.getName()
+ " declares multiple JSON fields named '"
+ duplicateName
+ "'; conflict is caused by fields "
+ ReflectionHelper.fieldToString(field1)
+ " and "
+ ReflectionHelper.fieldToString(field2)
+ "\nSee "
+ TroubleshootingGuide.createUrl("duplicate-fields"));
}
private FieldsData getBoundFields(
Gson context, TypeToken<?> type, Class<?> raw, boolean blockInaccessible, boolean isRecord) {
if (raw.isInterface()) {
return FieldsData.EMPTY;
}
View on GitHub (pinned to 8b8628c656)
Solutions
- Rename one of the conflicting fields or change its @SerializedName value to a distinct name.
- Mark one of the duplicate fields transient or exclude it with @Expose.
- Remove an unnecessary field redeclaration in the subclass.
- Adjust @SerializedName alternate arrays so no alias collides with another field's primary or alternate name.
Example fix
// before
class Base { @SerializedName("id") String uid; }
class Child extends Base { @SerializedName("id") long pk; }
// Child -> duplicate 'id'
// after
class Child extends Base { @SerializedName("pk") long pk; } Defensive patterns
Strategy: validation
Validate before calling
// Eagerly scan for duplicate JSON field names using the same naming policy
Set<String> names = new HashSet<>();
for (Class<?> c = Child.class; c != Object.class; c = c.getSuperclass()) {
for (Field f : c.getDeclaredFields()) {
String n = fieldNamingPolicy.translateName(f); // or @SerializedName value
if (!names.add(n)) throw new IllegalStateException("Duplicate JSON name: " + n);
}
} Type guard
null
Try / catch
try {
gson.fromJson(json, Child.class);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("declares multiple JSON fields named")) {
// rename conflicting field and rebuild adapter
} else throw e;
} Prevention
- Avoid redeclaring fields in subclasses; use distinct names.
- Keep @SerializedName alternates disjoint across the hierarchy.
- Run a startup self-test that builds a Gson and serializes every DTO to surface duplicates early.
- Prefer composition over inheritance for serialized types.
When it happens
Trigger: A subclass declares a field whose serializedName collides with an inherited field's serializedName; two fields in the same class are aliased to the same name via @SerializedName alternate; a FieldNamingPolicy remaps two distinct Java names to the same JSON name; explicit @SerializedName("x") on both a parent and child field.
Common situations: DTO inheritance hierarchies where child redeclares a parent field with the same @SerializedName; misconfigured alternates that point at an existing name; renaming a field while leaving the old @SerializedName on another field; cross-module shared base classes.
Related errors
- ReflectionAccessFilter does not permit using reflection for
- @SerializedName on " + methodDescription + " is not supporte
- Must only create direct subclasses of TypeToken
- Invalid style: {style}
- Class {typeAdapter.getClass().getName()} does not implement
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/81b88674afae8e44.json.
Report an issue: GitHub.