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

  1. Ensure STATIC fields remain excluded: excludeFieldsWithModifiers(Modifier.STATIC | Modifier.TRANSIENT) (the default)
  2. Mark the static final field with @Expose(serialize = false, deserialize = false) or transient
  3. 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

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


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/d509e4f2322fbffd. Report an issue: GitHub.