google/gson · error · UnsupportedOperationException
Attempted to deserialize a java.lang.Class. Forgot to regist
Error message
Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?\nSee " + TroubleshootingGuide.createUrl("java-lang-class-unsupported") What it means
The built-in CLASS TypeAdapter refuses to deserialize a java.lang.Class. Reading a Class reference from arbitrary JSON is unsafe (effectively Class.forName on attacker-controlled input) so Gson blocks it with UnsupportedOperationException unless the user explicitly registers a TypeAdapter<Class>. Thrown on read at line 81.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java:81
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 8b8628c656)
Solutions
- Register a custom, security-conscious TypeAdapter<Class<?>> that validates allowed class names before Class.forName.
- Replace the Class field with a String identifier and resolve the class through a controlled lookup.
- Exclude the Class field from deserialization (transient / @Expose(deserialize=false)).
- Use RuntimeTypeAdapterFactory for polymorphic typing instead of carrying Class in JSON.
Example fix
// before
class Job { Class<? extends Task> taskType; }
gson.fromJson(json, Job.class); // throws on read
// after
class Job { String taskTypeName; }
// resolve later via a whitelist
Class<? extends Task> resolve(Job j) {
return ALLOWED.get(j.taskTypeName);
} Defensive patterns
Strategy: validation
Validate before calling
// Reject incoming JSON targeting a Class field before deserialization
if (targetTypeHasClassField(MyType.class) && jsonContainsValueAtClassField(json)) {
throw new IllegalArgumentException("Refusing to deserialize Class from JSON");
} Type guard
null
Try / catch
try {
gson.fromJson(json, Job.class);
} catch (UnsupportedOperationException e) {
if (e.getMessage().startsWith("Attempted to deserialize a java.lang.Class")) {
// register a whitelisting Class adapter if you truly need this
} else throw e;
} Prevention
- Never deserialize Class from untrusted JSON; carry a String identifier and resolve via a whitelist.
- Exclude Class fields from deserialization.
- Use RuntimeTypeAdapterFactory for polymorphism instead of embedded Class refs.
- If a Class adapter is necessary, validate the name against an allow-list before Class.forName.
When it happens
Trigger: Deserializing JSON into a type that has a Class<?> field when the JSON contains a value at that position, and no custom Class adapter is registered. Also triggered by fromJson targeting a Class directly (gson.fromJson(json, Class.class)).
Common situations: Round-tripping serialized DTOs that include a Class field; deserializing config that references handler classes by type; frameworks that embed type metadata; polymorphic dispatch attempted via a Class field instead of a RuntimeTypeAdapterFactory.
Related errors
- Attempted to serialize java.lang.Class: " + value.getName()
- Failed parsing '" + s + "' as InetAddress; at path " + in.ge
- duplicate key: {key}
- Expecting number, got: " + jsonToken + "; at path " + in.get
- Unexpected token: " + peeked
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/d86930de8adecdcf.json.
Report an issue: GitHub.