{"record":{"id":"89551b2767efafc2","repo":"google/gson","slug":"cannot-deserialize-basetype-because-it-does-not","errorCode":null,"errorMessage":"cannot deserialize ${baseType} because it does not define a field named ${typeFieldName}","messagePattern":"cannot deserialize (.+?) because it does not define a field named (.+?)","errorType":"exception","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java","lineNumber":276,"sourceCode":"    for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {\n      TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));\n      labelToDelegate.put(entry.getKey(), delegate);\n      subtypeToDelegate.put(entry.getValue(), delegate);\n    }\n\n    return new TypeAdapter<R>() {\n      @Override\n      public R read(JsonReader in) throws IOException {\n        JsonElement jsonElement = jsonElementAdapter.read(in);\n        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      }","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L258-L294","documentation":"Thrown during deserialization by RuntimeTypeAdapterFactory when the incoming JSON object has no member matching the configured typeFieldName (the discriminator). The adapter reads the whole object into a JsonElement, then removes (or gets) the discriminator field; if it is absent it cannot decide which concrete subtype to instantiate, so it throws a JsonParseException. This guards against ambiguous or malformed polymorphic payloads.","triggerScenarios":"Deserializing JSON that was serialized without the discriminator (e.g. produced by a plain Gson without the factory, or by another system); using a typeFieldName that differs between writer and reader (case mismatch included); payload only contains base-type fields; JSON was trimmed/manipulated dropping the type field.","commonSituations":"Producer and consumer disagree on the discriminator key (\"type\" vs \"@type\" vs \"kind\"); migrating an API to polymorphic encoding while old persisted data lacks the field; a third party sends the base type only; rename refactor of typeFieldName on one side only.","solutions":["Confirm the incoming JSON object actually contains the discriminator key configured via RuntimeTypeAdapterFactory.of(base, typeFieldName).","Align the typeFieldName on both the serializing and deserializing Gson instances (spelling AND case).","If legacy data lacks the field, pre-process: parse to JsonObject, detect missing discriminator, inject a default label, then deserialize.","If payloads legitimately omit the discriminator, do not route them through this adapter (deserialize as the concrete type directly)."],"exampleFix":"// before: factory expects \"type\" but JSON has none\nfactory = RuntimeTypeAdapterFactory.of(Shape.class, \"type\");\n// {\"width\":10,\"height\":5} -> throws\n\n// after: ensure producer emits the discriminator, or align the key\nfactory = RuntimeTypeAdapterFactory.of(Shape.class, \"@type\");\n// and incoming JSON: {\"@type\":\"Rectangle\",\"width\":10,\"height\":5}","handlingStrategy":"validation","validationCode":"// Ensure discriminator is present before handing to Gson\nString discriminator = \"type\"; // must match factory config\nJsonElement el = JsonParser.parseString(json);\nif (el.isJsonObject() && !el.getAsJsonObject().has(discriminator)) {\n  throw new IllegalArgumentException(\"Missing discriminator '\" + discriminator + \"' in JSON\");\n}\nShape s = gson.fromJson(json, Shape.class);","typeGuard":null,"tryCatchPattern":"try {\n  gson.fromJson(json, Shape.class);\n} catch (JsonParseException e) {\n  if (e.getMessage().contains(\"does not define a field named\")) { /* re-route / default */ }\n  else throw e;\n}","preventionTips":["Pin the typeFieldName as a shared constant between producer and consumer.","Write a contract test asserting produced JSON contains the discriminator.","For legacy data lacking the field, transform the JsonObject to inject a default label before deserializing."],"tags":["runtime-type-adapter","polymorphism","deserialization","discriminator"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}