HMCL-dev/HMCL · error · JsonParseException
json.toString()
Error message
json.toString()
What it means
The Gson deserializer for JavaLocalFiles.Local requires the JSON element to be an object; a JsonParseException wrapping json.toString() is thrown when the element is an array, string, number, boolean, or null. This is a shape guard before reading the polymorphic `type` discriminator. It indicates corrupt or malformed data in a file describing a local Java installation entry.
Solutions
- Open the JSON file and fix the malformed entry so it is an object with a `type` field ("file", "directory", or "link").
- Restore the file from backup or delete it so the library regenerates defaults.
- Validate the whole file with a JSON linter/schema check before loading it in the app.
- If generating such files programmatically, always serialize through JavaLocalFiles.Serializer rather than writing raw JSON.
Example fix
// before
"java": "path/to/jdk"
// after
"java": { "type": "file", "sha1": "...", "size": 12345 } Defensive patterns
Strategy: validation
Validate before calling
JsonElement el = JsonParser.parseString(raw);
if (!el.isJsonObject()) {
throw new IllegalArgumentException("entry must be a JSON object, got: " + el);
} Try / catch
try {
Local local = gson.fromJson(raw, JavaLocalFiles.Local.class);
} catch (JsonParseException e) {
LOG.warning("Malformed local-java entry (must be an object with type): " + e.getMessage());
// skip entry or restore from backup
} Prevention
- Validate JSON files with a schema/linter before loading.
- Only produce these entries via JavaLocalFiles.Serializer.serialize, never raw string building.
- Back up java metadata files before hand-editing.
- Handle null/array/string JSON gracefully when pre-processing unknown input.
When it happens
Trigger: Gson deserializing a JSON value that is not an object (e.g. a bare string, array, or null literal) into type JavaLocalFiles.Local via the Serializer.deserialize adapter.
Common situations: A hand-edited or corrupted java-related JSON file (java repositories / custom runtime lists) where an entry was replaced by a scalar or array; an older schema version that stored entries differently; a truncation or merge conflict that broke the JSON structure.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Config is not an object:
- Protected payload is not a
- Expected JsonObject but got
- PortablePath must be a string: " + in.peek()
- this.getClass() + " cannot be deserialized to " + type
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/b0696511cd9bb954.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/java/JavaLocalFiles.java:98
public static class Serializer implements JsonSerializer<Local>, JsonDeserializer<Local> {
@Override
public JsonElement serialize(Local src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("type", src.getType());
if (src instanceof LocalFile) {
obj.addProperty("sha1", ((LocalFile) src).getSha1());
obj.addProperty("size", ((LocalFile) src).getSize());
} else if (src instanceof LocalLink) {
obj.addProperty("target", ((LocalLink) src).getTarget());
}
return obj;
}
@Override
public Local deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!json.isJsonObject())
throw new JsonParseException(json.toString());
JsonObject obj = json.getAsJsonObject();
if (!obj.has("type"))
throw new JsonParseException(json.toString());
String type = obj.getAsJsonPrimitive("type").getAsString();
try {
switch (type) {
case "file": {
String sha1 = obj.getAsJsonPrimitive("sha1").getAsString();
long size = obj.getAsJsonPrimitive("size").getAsLong();
return new LocalFile(sha1, size);
}
case "directory": {
return new LocalDirectory();
}
case "link": {View on GitHub (pinned to 24702dc5a0)