{"id":"6b2f33ec193db8a7","repo":"google/gson","slug":"cannot-serialize-srctype-getname-did-you-forg","errorCode":null,"errorMessage":"cannot serialize {srcType.getName()}; did you forget to register a subtype?","messagePattern":"cannot serialize (.+?); did you forget to register a subtype\\?","errorType":"exception","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java","lineNumber":303,"sourceCode":"        if (delegate == null) {\n          throw new JsonParseException(\n              \"cannot deserialize \"\n                  + baseType\n                  + \" subtype named \"\n                  + label\n                  + \"; did you forget to register a subtype?\");\n        }\n        return delegate.fromJsonTree(jsonElement);\n      }\n\n      @Override\n      public void write(JsonWriter out, R value) throws IOException {\n        Class<?> srcType = value.getClass();\n        String label = subtypeToLabel.get(srcType);\n        @SuppressWarnings(\"unchecked\") // registration requires that subtype extends T\n        TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);\n        if (delegate == null) {\n          throw new JsonParseException(\n              \"cannot serialize \" + srcType.getName() + \"; did you forget to register a subtype?\");\n        }\n        JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();\n\n        if (maintainType) {\n          jsonElementAdapter.write(out, jsonObject);\n          return;\n        }\n\n        JsonObject clone = new JsonObject();\n\n        if (jsonObject.has(typeFieldName)) {\n          throw new JsonParseException(\n              \"cannot serialize \"\n                  + srcType.getName()\n                  + \" because it already defines a field named \"\n                  + typeFieldName);\n        }","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L285-L321","documentation":"Thrown during serialization by RuntimeTypeAdapterFactory's write() when the runtime (actual) class of the object being serialized was not registered as a subtype. The adapter looks up value.getClass() in its subtypeToLabel map; an unregistered class cannot be assigned a discriminator label, so serialization is aborted. This is a JsonParseException thrown from toJson/toJsonTree.","triggerScenarios":"Calling gson.toJson(new Triangle(), Shape.class) when only Circle and Rectangle were registered; passing a subclass instance whose parent was registered but the concrete class was not (registration is by exact class, not by assignability unless recognizeSubtypes() is set, and even then write() uses the exact runtime class); instantiating an anonymous subclass of a registered type.","commonSituations":"Adding a new subclass but forgetting to register it; serializing an anonymous or lambda-derived subclass of a registered type; using a mocking framework (Mockito) that creates a synthetic subclass; protobuf/AutoValue generated subclasses that differ from the registered class.","solutions":["Register the concrete runtime class: factory.registerSubtype(Triangle.class).","Check value.getClass() at the call site before serializing if the type set is dynamic.","Avoid anonymous subclasses of registered base types, or register their exact generated names (impractical; prefer named classes).","If using code generation (AutoValue, protobuf), register the generated subclass, not the base."],"exampleFix":"// before\nRuntimeTypeAdapterFactory<Shape> f = RuntimeTypeAdapterFactory.of(Shape.class)\n    .registerSubtype(Circle.class);\nGson gson = new GsonBuilder().registerTypeAdapterFactory(f).create();\nString json = gson.toJson(new Triangle(), Shape.class); // throws\n\n// after\nRuntimeTypeAdapterFactory<Shape> f = RuntimeTypeAdapterFactory.of(Shape.class)\n    .registerSubtype(Circle.class)\n    .registerSubtype(Triangle.class);\nString json = gson.toJson(new Triangle(), Shape.class);","handlingStrategy":"type-guard","validationCode":"// Validate the runtime class is registered before serializing\nSet<Class<?>> registered = Set.of(Circle.class, Rectangle.class);\nObject value = new Triangle();\nif (!registered.contains(value.getClass())) {\n  throw new IllegalArgumentException(\"Class not registered: \" + value.getClass());\n}\nString json = gson.toJson(value, Shape.class);","typeGuard":"static boolean isRegisteredSubtype(Object value, Set<Class<?>> registered) {\n  return value != null && registered.contains(value.getClass());\n}","tryCatchPattern":"try {\n  String json = gson.toJson(value, Shape.class);\n} catch (JsonParseException e) {\n  if (e.getMessage().contains(\"cannot serialize\") && e.getMessage().contains(\"register a subtype\")) {\n    // value's runtime class not registered; register it or reject\n  } else throw e;\n}","preventionTips":["Always serialize via gson.toJson(obj, BaseType.class) and ensure the concrete class is registered.","Avoid anonymous subclasses of registered base types; use named classes.","If using code generation (AutoValue, protobuf), register the generated subclass.","Keep a registry/enum of allowed concrete types and check membership before serializing."],"tags":["runtime-type-adapter","polymorphic","serialization","unregistered-subtype"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}