google/gson · error · JsonIOException

Cannot set value of 'static final' " + fieldDescription

Error message

Cannot set value of 'static final' " + fieldDescription

What it means

Thrown during deserialization when Gson attempts to write an incoming JSON value into a field declared `static final`. The JVM forbids reflective Field.set on static final fields (except a few special cases), so Gson detects the modifier combination and throws a JsonIOException before attempting the set. The check happens only when the ReflectionAccessFilter did not block access (otherwise checkAccessible runs first).

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 8b8628c656)

Solutions

  1. Restore the default exclusion by keeping Modifier.STATIC in excludeFieldsWithModifiers, or simply do not customize it.
  2. Mark the field transient so Gson ignores it.
  3. Annotate the field with @Expose(serialize=false, deserialize=false) and use excludeFieldsWithoutExposeAnnotation.
  4. Refactor the constant into an enum or a non-static instance field if you truly need it in JSON.

Example fix

// before
new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.FINAL) // dropped STATIC
    .create()
    .fromJson(json, Config.class); // throws on `public static final int MAX`

// after: keep default (STATIC | TRANSIENT) or add @ transient
public static transient int max; // or just remove 'static final'
Defensive patterns

Strategy: validation

Validate before calling

// Detect static final fields that would be deserialized
for (Field f : Config.class.getDeclaredFields()) {
  int m = f.getModifiers();
  if (Modifier.isStatic(m) && Modifier.isFinal(m)) {
    throw new IllegalStateException("Cannot deserialize into static final " + f);
  }
}

Type guard

null

Try / catch

try {
  gson.fromJson(json, Config.class);
} catch (JsonIOException e) {
  if (e.getMessage().startsWith("Cannot set value of 'static final'")) {
    // skip / use defaults / fix modifier config
  } else throw e;
}

Prevention

When it happens

Trigger: Triggered in BoundField.readIntoField (ReflectiveTypeAdapterFactory.java:278) when a deserialized field is static+final and a non-null value is read from JSON. Static fields are excluded by default, but GsonBuilder.excludeFieldsWithModifiers can re-include them; also reachable when fields are not excluded via Excluder.

Common situations: Customizing GsonBuilder.excludeFieldsWithModifiers to drop Modifier.STATIC from the exclusion list; serializing/deserializing classes with constants like `public static final int MAX`; mixing config-holder classes that reuse static finals as JSON keys.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/92c721169770b2d3.json. Report an issue: GitHub.