google/gson · error · JsonIOException
Cannot set value of 'static final' ${fieldDescription}
Error message
Cannot set value of 'static final' ${fieldDescription} What it means
During deserialization into a regular (non-record) class, Gson sets each field via Field.set. The JVM prevents setting static final fields even after setAccessible(true), so Gson detects this case explicitly and throws JsonIOException with a clear message instead of letting Field.set fail with a confusing IllegalAccessException.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java:283
+ "' of primitive type; at path "
+ reader.getPath());
}
target[index] = fieldValue;
}
@Override
void readIntoField(JsonReader reader, Object target)
throws IOException, IllegalAccessException {
Object fieldValue = typeAdapter.read(reader);
if (fieldValue != null || !isPrimitive) {
if (blockInaccessible) {
checkAccessible(target, field);
} else if (isStaticFinalField) {
// Reflection does not permit setting value of `static final` field, even after calling
// `setAccessible`
// Handle this here to avoid causing IllegalAccessException when calling `Field.set`
String fieldDescription = ReflectionHelper.getAccessibleObjectDescription(field, false);
throw new JsonIOException("Cannot set value of 'static final' " + fieldDescription);
}
field.set(target, fieldValue);
}
}
};
}
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;View on GitHub (pinned to 310ac341f2)
Solutions
- Ensure STATIC fields remain excluded: excludeFieldsWithModifiers(Modifier.STATIC | Modifier.TRANSIENT) (the default)
- Mark the static final field with @Expose(serialize = false, deserialize = false) or transient
- Register a custom TypeAdapter for the type to control deserialization without reflection
Example fix
// before
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT) // STATIC no longer excluded
.create();
gson.fromJson("{\"CONST\":5}", Config.class); // throws if Config has 'static final int CONST'
// after
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC | Modifier.TRANSIENT)
.create(); Defensive patterns
Strategy: validation
Validate before calling
// Verify the Gson configuration does not exclude STATIC fields int modifiers = Modifier.STATIC | Modifier.TRANSIENT; // correct default // If you must customize, never drop STATIC: // .excludeFieldsWithModifiers(Modifier.TRANSIENT) // BAD — re-add Modifier.STATIC
Prevention
- Keep Modifier.STATIC in excludeFieldsWithModifiers — it is part of the default for a reason
- Audit any call to excludeFieldsWithModifiers to confirm STATIC remains excluded
- Mark constant fields transient or @Expose(serialize=false, deserialize=false) as a belt-and-suspenders measure
When it happens
Trigger: Deserializing JSON into a class that has a static final field which Gson is not excluding — this only happens when excludeFieldsWithModifiers was configured to remove the default STATIC exclusion.
Common situations: A developer calls GsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT) (dropping Modifier.STATIC from the exclusion set), causing Gson to attempt writing to static final constant fields.
Related errors
- GSON (${GsonBuildConfig.VERSION}) cannot handle ${type}
- Cannot allocate ${c}. Usage of JDK sun.misc.Unsafe is enable
- Could not find the index in the constructor '${constructor}'
- types and labels must be unique
- cannot deserialize ${baseType} because it does not define a
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/d509e4f2322fbffd.
Report an issue: GitHub.