HMCL-dev/HMCL · error · JsonParseException
Cannot deserialize
Error message
Cannot deserialize
What it means
JsonTypeAdapterFactory's read() selects a subtype adapter by looking up a type-label property in the JSON object. This JsonParseException is thrown when the JSON object lacks the field named by @JsonType(property=...), so the polymorphic subtype cannot be determined.
Solutions
- Add the discriminator field (jsonType.property()) with a valid label to the JSON object.
- Verify the property name in the @JsonType annotation matches what the JSON actually contains.
- Re-serialize the object with the same Gson instance so the type label is written.
- Handle legacy payloads by migrating them to include the type field before deserialization.
Example fix
// before
{"name": "x"}
// after
{"type": "concrete", "name": "x"} Defensive patterns
Strategy: validation
Validate before calling
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
if (!obj.has("type") || obj.get("type").isJsonNull())
throw new IllegalArgumentException("Missing discriminator field 'type'"); Type guard
static boolean hasTypeLabel(JsonObject obj, String property) {
JsonElement e = obj.get(property);
return e != null && e.isJsonPrimitive() && e.getAsJsonPrimitive().isString();
} Try / catch
try {
return gson.fromJson(json, PolyType.class);
} catch (JsonParseException e) {
log.error("Missing type discriminator: {}", e.getMessage());
} Prevention
- Serialize polymorphic objects with the same Gson setup so the discriminator is always written.
- Keep the @JsonType property name stable across versions.
- Validate payloads contain the discriminator field before deserializing.
When it happens
Trigger: Deserializing a polymorphic type whose JSON is missing the discriminator property, e.g. an object without the "type" field the factory was configured with.
Common situations: Hand-written JSON missing the type tag, an upstream API dropping the field, serialization produced by a version where the property had a different name.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Config is not an object:
- Invalid JSON schema
- Json object cannot be null.
- json.toString()
- PortablePath must be a string: " + in.peek()
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/a7eab4fdcfd381da.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonTypeAdapterFactory.java:73
TypeAdapter<T> delegate = (TypeAdapter<T>) classTypeAdapterMap.get(type);
if (delegate == null) {
throw new JsonParseException("Cannot serialize " + type.getName() + ". Please check your @JsonType configuration");
}
JsonSubtype subtype = classJsonSubtypeMap.get(type);
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
if (jsonObject.has(jsonType.property())) {
throw new JsonParseException("Cannot serialize " + type.getName() + ". Because it has already defined a field named '" + jsonType.property() + "'");
}
jsonObject.add(jsonType.property(), new JsonPrimitive(subtype.name()));
Streams.write(jsonObject, out);
}
@Override
public T read(JsonReader in) {
JsonElement jsonElement = Streams.parse(in);
JsonElement typeLabelElement = jsonElement.getAsJsonObject().get(jsonType.property());
if (typeLabelElement == null) {
throw new JsonParseException("Cannot deserialize " + type + ". Because it does not define a field named '" + jsonType.property() + "'");
}
String typeLabel = typeLabelElement.getAsString();
@SuppressWarnings("unchecked")
TypeAdapter<T> delegate = (TypeAdapter<T>) labelTypeAdapterMap.get(typeLabel);
if (delegate == null) {
throw new JsonParseException("Cannot deserialize " + type + " with subtype '" + typeLabel + "'");
}
return delegate.fromJsonTree(jsonElement);
}
};
}
private <T> TypeAdapter<T> createForJsonSubtype(Gson gson, TypeToken<T> type) {
Class<? super T> rawType = type.getRawType();
if (rawType.getSuperclass() == null) return null;
JsonType jsonType = rawType.getSuperclass().getDeclaredAnnotation(JsonType.class);
if (jsonType == null)View on GitHub (pinned to 24702dc5a0)