google/gson · error · JsonParseException

cannot deserialize because it does not define a field named

Error message

cannot deserialize ${baseType} because it does not define a field named ${typeFieldName}

What it means

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.

Solutions

  1. Confirm the incoming JSON object actually contains the discriminator key configured via RuntimeTypeAdapterFactory.of(base, typeFieldName).
  2. Align the typeFieldName on both the serializing and deserializing Gson instances (spelling AND case).
  3. If legacy data lacks the field, pre-process: parse to JsonObject, detect missing discriminator, inject a default label, then deserialize.
  4. If payloads legitimately omit the discriminator, do not route them through this adapter (deserialize as the concrete type directly).

Example fix

// before: factory expects "type" but JSON has none
factory = RuntimeTypeAdapterFactory.of(Shape.class, "type");
// {"width":10,"height":5} -> throws

// after: ensure producer emits the discriminator, or align the key
factory = RuntimeTypeAdapterFactory.of(Shape.class, "@type");
// and incoming JSON: {"@type":"Rectangle","width":10,"height":5}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure discriminator is present before handing to Gson
String discriminator = "type"; // must match factory config
JsonElement el = JsonParser.parseString(json);
if (el.isJsonObject() && !el.getAsJsonObject().has(discriminator)) {
  throw new IllegalArgumentException("Missing discriminator '" + discriminator + "' in JSON");
}
Shape s = gson.fromJson(json, Shape.class);

Try / catch

try {
  gson.fromJson(json, Shape.class);
} catch (JsonParseException e) {
  if (e.getMessage().contains("does not define a field named")) { /* re-route / default */ }
  else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/89551b2767efafc2. Report an issue: GitHub.

Appendix: source

Thrown at extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java:276

    for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
      TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
      labelToDelegate.put(entry.getKey(), delegate);
      subtypeToDelegate.put(entry.getValue(), delegate);
    }

    return new TypeAdapter<R>() {
      @Override
      public R read(JsonReader in) throws IOException {
        JsonElement jsonElement = jsonElementAdapter.read(in);
        JsonElement labelJsonElement;
        if (maintainType) {
          labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
        } else {
          labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
        }

        if (labelJsonElement == null) {
          throw new JsonParseException(
              "cannot deserialize "
                  + baseType
                  + " because it does not define a field named "
                  + typeFieldName);
        }
        String label = labelJsonElement.getAsString();
        @SuppressWarnings("unchecked") // registration requires that subtype extends T
        TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
        if (delegate == null) {
          throw new JsonParseException(
              "cannot deserialize "
                  + baseType
                  + " subtype named "
                  + label
                  + "; did you forget to register a subtype?");
        }
        return delegate.fromJsonTree(jsonElement);
      }

View on GitHub (pinned to 310ac341f2)