{"id":"d86930de8adecdcf","repo":"google/gson","slug":"attempted-to-deserialize-a-java-lang-class-forgot","errorCode":null,"errorMessage":"Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?\\nSee \" + TroubleshootingGuide.createUrl(\"java-lang-class-unsupported\")","messagePattern":"Attempted to deserialize a java\\.lang\\.Class\\. Forgot to register a type adapter\\?\\\\nSee \" \\+ TroubleshootingGuide\\.createUrl\\(\"java-lang-class-unsupported\"\\)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java","lineNumber":81,"sourceCode":"    throw new UnsupportedOperationException();\n  }\n\n  @SuppressWarnings(\"rawtypes\")\n  public static final TypeAdapter<Class> CLASS =\n      new TypeAdapter<Class>() {\n        @Override\n        public void write(JsonWriter out, Class value) throws IOException {\n          throw new UnsupportedOperationException(\n              \"Attempted to serialize java.lang.Class: \"\n                  + value.getName()\n                  + \". Forgot to register a type adapter?\"\n                  + \"\\nSee \"\n                  + TroubleshootingGuide.createUrl(\"java-lang-class-unsupported\"));\n        }\n\n        @Override\n        public Class read(JsonReader in) throws IOException {\n          throw new UnsupportedOperationException(\n              \"Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?\"\n                  + \"\\nSee \"\n                  + TroubleshootingGuide.createUrl(\"java-lang-class-unsupported\"));\n        }\n      }.nullSafe();\n\n  public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);\n\n  public static final TypeAdapter<BitSet> BIT_SET =\n      new TypeAdapter<BitSet>() {\n        @Override\n        public BitSet read(JsonReader in) throws IOException {\n          BitSet bitset = new BitSet();\n          in.beginArray();\n          int i = 0;\n          JsonToken tokenType = in.peek();\n          while (tokenType != JsonToken.END_ARRAY) {\n            boolean set;","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java#L63-L99","documentation":"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.","triggerScenarios":"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)).","commonSituations":"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.","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."],"exampleFix":"// before\nclass Job { Class<? extends Task> taskType; }\ngson.fromJson(json, Job.class); // throws on read\n\n// after\nclass Job { String taskTypeName; }\n// resolve later via a whitelist\nClass<? extends Task> resolve(Job j) {\n  return ALLOWED.get(j.taskTypeName);\n}","handlingStrategy":"validation","validationCode":"// Reject incoming JSON targeting a Class field before deserialization\nif (targetTypeHasClassField(MyType.class) && jsonContainsValueAtClassField(json)) {\n  throw new IllegalArgumentException(\"Refusing to deserialize Class from JSON\");\n}","typeGuard":"null","tryCatchPattern":"try {\n  gson.fromJson(json, Job.class);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().startsWith(\"Attempted to deserialize a java.lang.Class\")) {\n    // register a whitelisting Class adapter if you truly need this\n  } else throw e;\n}","preventionTips":["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."],"tags":["gson","deserialization","class","unsupported","security"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}