google/gson · error · JsonParseException

cannot serialize {srcType.getName()}; did you forget to regi

Error message

cannot serialize {srcType.getName()}; did you forget to register a subtype?

What it means

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.

Source

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

        if (delegate == null) {
          throw new JsonParseException(
              "cannot deserialize "
                  + baseType
                  + " subtype named "
                  + label
                  + "; did you forget to register a subtype?");
        }
        return delegate.fromJsonTree(jsonElement);
      }

      @Override
      public void write(JsonWriter out, R value) throws IOException {
        Class<?> srcType = value.getClass();
        String label = subtypeToLabel.get(srcType);
        @SuppressWarnings("unchecked") // registration requires that subtype extends T
        TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
        if (delegate == null) {
          throw new JsonParseException(
              "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?");
        }
        JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();

        if (maintainType) {
          jsonElementAdapter.write(out, jsonObject);
          return;
        }

        JsonObject clone = new JsonObject();

        if (jsonObject.has(typeFieldName)) {
          throw new JsonParseException(
              "cannot serialize "
                  + srcType.getName()
                  + " because it already defines a field named "
                  + typeFieldName);
        }

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Register the concrete runtime class: factory.registerSubtype(Triangle.class).
  2. Check value.getClass() at the call site before serializing if the type set is dynamic.
  3. Avoid anonymous subclasses of registered base types, or register their exact generated names (impractical; prefer named classes).
  4. If using code generation (AutoValue, protobuf), register the generated subclass, not the base.

Example fix

// before
RuntimeTypeAdapterFactory<Shape> f = RuntimeTypeAdapterFactory.of(Shape.class)
    .registerSubtype(Circle.class);
Gson gson = new GsonBuilder().registerTypeAdapterFactory(f).create();
String json = gson.toJson(new Triangle(), Shape.class); // throws

// after
RuntimeTypeAdapterFactory<Shape> f = RuntimeTypeAdapterFactory.of(Shape.class)
    .registerSubtype(Circle.class)
    .registerSubtype(Triangle.class);
String json = gson.toJson(new Triangle(), Shape.class);
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the runtime class is registered before serializing
Set<Class<?>> registered = Set.of(Circle.class, Rectangle.class);
Object value = new Triangle();
if (!registered.contains(value.getClass())) {
  throw new IllegalArgumentException("Class not registered: " + value.getClass());
}
String json = gson.toJson(value, Shape.class);

Type guard

static boolean isRegisteredSubtype(Object value, Set<Class<?>> registered) {
  return value != null && registered.contains(value.getClass());
}

Try / catch

try {
  String json = gson.toJson(value, Shape.class);
} catch (JsonParseException e) {
  if (e.getMessage().contains("cannot serialize") && e.getMessage().contains("register a subtype")) {
    // value's runtime class not registered; register it or reject
  } else throw e;
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/6b2f33ec193db8a7.json. Report an issue: GitHub.