{"id":"2c119bda15ec6227","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/8b8628c65699bc4421696183c62ae0c1b9b281dc/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L258-L294","documentation":"Thrown during deserialization by RuntimeTypeAdapterFactory's read() when the incoming JSON object contains no field whose name matches the configured typeFieldName (default \"type\"). The adapter relies on that discriminator field to decide which registered subtype to instantiate; without it there is no way to pick the right class. This is a JsonParseException surfaced through gson.fromJson.","triggerScenarios":"Deserializing JSON that was serialized without the runtime type adapter (plain gson.toJson produced no \"type\" field); serializing with gson.toJson(obj, Shape.class) but then reading back through a Gson instance that lacks the RuntimeTypeAdapterFactory registered; the JSON producer renamed or omitted the discriminator field; maintainType=false and the field was stripped on the producer side.","commonSituations":"Producer and consumer Gson instances are configured differently (producer has no factory); an external system sends hand-built JSON without the type field; field name mismatch because the producer used of(Shape.class, \"kind\") and the consumer used of(Shape.class, \"type\"); legacy JSON predating the polymorphic adapter.","solutions":["Ensure the same RuntimeTypeAdapterFactory (same base type AND same typeFieldName) is registered on the Gson instance used for both serialization and deserialization.","Verify the JSON actually contains the discriminator field, e.g. jsonObject.has(\"type\"), before deserializing.","If consuming external JSON, serialize a sample object first and inspect the output to confirm the exact field name and label values.","If you must accept JSON without the discriminator, write a pre-processing step (JsonElement transform) that injects the field, or use a different deserialization strategy."],"exampleFix":"// before: producer lacks the factory, so JSON has no \"type\" field\nGson producer = new Gson();\nString json = producer.toJson(diamond, Shape.class);\nShape s = consumerGson.fromJson(json, Shape.class); // throws\n\n// after: producer and consumer share the factory\nRuntimeTypeAdapterFactory<Shape> f = RuntimeTypeAdapterFactory.of(Shape.class, \"type\")\n    .registerSubtype(Diamond.class);\nGson producer = new GsonBuilder().registerTypeAdapterFactory(f).create();\nGson consumer = new GsonBuilder().registerTypeAdapterFactory(f).create();\nString json = producer.toJson(diamond, Shape.class);\nShape s = consumer.fromJson(json, Shape.class);","handlingStrategy":"validation","validationCode":"// Validate the JSON has the discriminator before deserializing\nString typeFieldName = \"type\";\nJsonElement root = JsonParser.parseString(json);\nif (!root.isJsonObject() || !root.getAsJsonObject().has(typeFieldName)) {\n  throw new IllegalArgumentException(\"Missing discriminator field '\" + typeFieldName + \"'\");\n}\nShape s = gson.fromJson(root, Shape.class);","typeGuard":"static boolean hasDiscriminator(String json, String typeFieldName) {\n  try {\n    JsonElement e = JsonParser.parseString(json);\n    return e.isJsonObject() && e.getAsJsonObject().has(typeFieldName);\n  } catch (JsonSyntaxException ex) { return false; }\n}","tryCatchPattern":"try {\n  Shape s = gson.fromJson(json, Shape.class);\n} catch (JsonParseException e) {\n  if (e.getMessage().contains(\"does not define a field named\")) {\n    // handle missing discriminator: log and reject, or retry with a default subtype\n  } else throw e;\n}","preventionTips":["Share the exact same RuntimeTypeAdapterFactory (same base type + typeFieldName) across producer and consumer Gson instances.","Serialize a sample object and assert the JSON contains the discriminator field as a contract test.","When integrating with external JSON, document and validate the required discriminator field name and allowed labels.","Use a JsonElement pre-check rather than relying on the exception for control flow."],"tags":["runtime-type-adapter","polymorphic","deserialization","missing-field"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}