{"record":{"id":"a7eab4fdcfd381da","repo":"HMCL-dev/HMCL","slug":"cannot-deserialize","errorCode":null,"errorMessage":"Cannot deserialize ","messagePattern":"Cannot deserialize ","errorType":"exception","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonTypeAdapterFactory.java","lineNumber":73,"sourceCode":"                TypeAdapter<T> delegate = (TypeAdapter<T>) classTypeAdapterMap.get(type);\n                if (delegate == null) {\n                    throw new JsonParseException(\"Cannot serialize \" + type.getName() + \". Please check your @JsonType configuration\");\n                }\n                JsonSubtype subtype = classJsonSubtypeMap.get(type);\n                JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();\n                if (jsonObject.has(jsonType.property())) {\n                    throw new JsonParseException(\"Cannot serialize \" + type.getName() + \". Because it has already defined a field named '\" + jsonType.property() + \"'\");\n                }\n                jsonObject.add(jsonType.property(), new JsonPrimitive(subtype.name()));\n                Streams.write(jsonObject, out);\n            }\n\n            @Override\n            public T read(JsonReader in) {\n                JsonElement jsonElement = Streams.parse(in);\n                JsonElement typeLabelElement = jsonElement.getAsJsonObject().get(jsonType.property());\n                if (typeLabelElement == null) {\n                    throw new JsonParseException(\"Cannot deserialize \" + type + \". Because it does not define a field named '\" + jsonType.property() + \"'\");\n                }\n                String typeLabel = typeLabelElement.getAsString();\n                @SuppressWarnings(\"unchecked\")\n                TypeAdapter<T> delegate = (TypeAdapter<T>) labelTypeAdapterMap.get(typeLabel);\n                if (delegate == null) {\n                    throw new JsonParseException(\"Cannot deserialize \" + type + \" with subtype '\" + typeLabel + \"'\");\n                }\n\n                return delegate.fromJsonTree(jsonElement);\n            }\n        };\n    }\n\n    private <T> TypeAdapter<T> createForJsonSubtype(Gson gson, TypeToken<T> type) {\n        Class<? super T> rawType = type.getRawType();\n        if (rawType.getSuperclass() == null) return null;\n        JsonType jsonType = rawType.getSuperclass().getDeclaredAnnotation(JsonType.class);\n        if (jsonType == null)","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonTypeAdapterFactory.java#L55-L91","documentation":"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.","triggerScenarios":"Deserializing a polymorphic type whose JSON is missing the discriminator property, e.g. an object without the \"type\" field the factory was configured with.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"name\": \"x\"}\n// after\n{\"type\": \"concrete\", \"name\": \"x\"}","handlingStrategy":"validation","validationCode":"JsonObject obj = JsonParser.parseString(json).getAsJsonObject();\nif (!obj.has(\"type\") || obj.get(\"type\").isJsonNull())\n    throw new IllegalArgumentException(\"Missing discriminator field 'type'\");","typeGuard":"static boolean hasTypeLabel(JsonObject obj, String property) {\n    JsonElement e = obj.get(property);\n    return e != null && e.isJsonPrimitive() && e.getAsJsonPrimitive().isString();\n}","tryCatchPattern":"try {\n    return gson.fromJson(json, PolyType.class);\n} catch (JsonParseException e) {\n    log.error(\"Missing type discriminator: {}\", e.getMessage());\n}","preventionTips":["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."],"tags":["gson","polymorphism","deserialization"],"backgroundTag":"missing-required-argument","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}