google/gson · error · UnsupportedOperationException

Attempted to deserialize a java.lang.Class. Forgot to…

Error message

Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?
See ${url}

What it means

Gson's built-in CLASS adapter throws UnsupportedOperationException on read because deserializing an arbitrary java.lang.Class from JSON is a security risk (class loading). The adapter fires whenever Gson encounters a Class field during deserialization and no custom adapter was registered.

Solutions

  1. Register a custom JsonDeserializer<Class<?>> that maps a class-name string to Class.forName with an allowlist
  2. Replace the Class<?> field with a String type identifier and resolve the class separately
  3. Mark the field with @Expose(deserialize = false) so Gson ignores it during deserialization

Example fix

// before
class Entity {
    String name;
    Class<?> type; // causes UnsupportedOperationException on deserialize
}
gson.fromJson("{\"type\":\"com.example.Foo\"}", Entity.class);

// after
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Class.class, (JsonDeserializer<Class<?>>) (j, t, c) ->
        Class.forName(j.getAsString()))
    .create();
Defensive patterns

Strategy: validation

Validate before calling

// Check model classes for Class fields before deserializing
public static boolean hasClassField(Class<?> type) {
    for (Field f : type.getDeclaredFields()) {
        if (f.getType() == Class.class && !Modifier.isTransient(f.getModifiers())) {
            return true; // will trigger UnsupportedOperationException on deserialize
        }
    }
    return false;
}

Try / catch

try {
    Entity e = gson.fromJson(json, Entity.class);
} catch (UnsupportedOperationException e) {
    if (e.getMessage().contains("Attempted to deserialize a java.lang.Class")) {
        // register a TypeAdapter<Class<?>> or change the field type to String
    }
}

Prevention

When it happens

Trigger: Deserializing JSON into an object that has a field of type Class<?> via gson.fromJson() without registering a custom TypeAdapter for Class.

Common situations: A DTO with a Class<?> field receives JSON input; polymorphic type handling that naively uses Class fields; legacy models migrated from a framework that supported class deserialization.

Related errors


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

Appendix: source

Thrown at gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java:83

    throw new UnsupportedOperationException();
  }

  @SuppressWarnings("rawtypes")
  public static final TypeAdapter<Class> CLASS =
      new TypeAdapter<Class>() {
        @Override
        public void write(JsonWriter out, Class value) throws IOException {
          throw new UnsupportedOperationException(
              "Attempted to serialize java.lang.Class: "
                  + value.getName()
                  + ". Forgot to register a type adapter?"
                  + "\nSee "
                  + TroubleshootingGuide.createUrl("java-lang-class-unsupported"));
        }

        @Override
        public Class read(JsonReader in) throws IOException {
          throw new UnsupportedOperationException(
              "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"
                  + "\nSee "
                  + TroubleshootingGuide.createUrl("java-lang-class-unsupported"));
        }
      }.nullSafe();

  public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);

  public static final TypeAdapter<BitSet> BIT_SET =
      new TypeAdapter<BitSet>() {
        @Override
        public BitSet read(JsonReader in) throws IOException {
          BitSet bitset = new BitSet();
          in.beginArray();
          int i = 0;
          JsonToken tokenType = in.peek();
          while (tokenType != JsonToken.END_ARRAY) {
            boolean set;

View on GitHub (pinned to 310ac341f2)