{"record":{"id":"c730bc36a23a19b2","repo":"google/gson","slug":"cannot-deserialize-basetype-subtype-named-lab","errorCode":null,"errorMessage":"cannot deserialize ${baseType} subtype named ${label}; did you forget to register a subtype?","messagePattern":"cannot deserialize (.+?) subtype named (.+?); 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":286,"sourceCode":"        JsonElement labelJsonElement;\n        if (maintainType) {\n          labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);\n        } else {\n          labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);\n        }\n\n        if (labelJsonElement == null) {\n          throw new JsonParseException(\n              \"cannot deserialize \"\n                  + baseType\n                  + \" because it does not define a field named \"\n                  + typeFieldName);\n        }\n        String label = labelJsonElement.getAsString();\n        @SuppressWarnings(\"unchecked\") // registration requires that subtype extends T\n        TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);\n        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?\");","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L268-L304","documentation":"Thrown during deserialization by RuntimeTypeAdapterFactory when the JSON discriminator value (the label) does not match any label registered via registerSubtype. The adapter looks up the delegate adapter keyed by the label string; a null result means the subtype is unknown to this factory, which is rejected as a security measure (no unregistered/injected types are instantiated).","triggerScenarios":"JSON carries a discriminator label that was never registered (e.g. \"Hexagon\" when only Rectangle/Circle are registered); producer added a new subtype but the consumer's factory was not updated; label case mismatch (\"rectangle\" vs \"Rectangle\"); trailing/whitespace differences in the label value.","commonSituations":"Rolling out a new polymorphic subtype on the server before updating client-side Gson configuration; case-sensitive label mismatch across locales/serializers; tampered or forward-incompatible payload sent by a newer API version; copy of registration list that dropped an entry.","solutions":["Register the subtype whose label appears in the JSON: factory.registerSubtype(TheClass, \"theLabelFromJson\").","Verify label exact spelling and case against the JSON value (labels are case sensitive per the Javadoc).","If forward compatibility is needed, pre-sanitize unknown labels to a safe default before deserializing, or deserialize into a known superset of registered types.","Add a unit test asserting every label the producer can emit is registered on the consumer."],"exampleFix":"// before: JSON has {\"type\":\"Hexagon\"} but it is not registered\nfactory.registerSubtype(Rectangle.class, \"Rectangle\");\nfactory.registerSubtype(Circle.class, \"Circle\");\n\n// after\nfactory.registerSubtype(Hexagon.class, \"Hexagon\");","handlingStrategy":"validation","validationCode":"// Validate the discriminator label against the registered set before deserializing\nSet<String> allowed = Set.of(\"Rectangle\", \"Circle\", \"Diamond\");\nJsonElement el = JsonParser.parseString(json);\nString label = el.isJsonObject() && el.getAsJsonObject().has(\"type\")\n    ? el.getAsJsonObject().get(\"type\").getAsString() : null;\nif (label == null || !allowed.contains(label)) {\n  throw new IllegalArgumentException(\"Unknown subtype label: \" + label);\n}","typeGuard":null,"tryCatchPattern":"try { gson.fromJson(json, Shape.class); }\ncatch (JsonParseException e) {\n  if (e.getMessage().contains(\"did you forget to register a subtype\")) {\n    // log unknown label, fall back to a safe default type or reject the request\n  } else throw e;\n}","preventionTips":["Keep the registered label set and the producer's emitted labels in one source of truth.","Treat unknown labels as untrusted input (security-sensitive): never auto-instantiate by class name.","Add tests covering every label the producer can emit."],"tags":["runtime-type-adapter","polymorphism","deserialization","security"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}