HMCL-dev/HMCL · error · JsonParseException

Unexpected json element:

Error message

Unexpected json element: 

What it means

LocalizedText.fromJson accepts JSON null, a string primitive, or an object of string values. Any other JsonElement kind (array, or unexpected element types) falls through all accepted branches and is rejected with this JsonParseException.

Solutions

  1. Replace the array with either a single string or an object mapping locale keys to strings.
  2. Check the element kind (isJsonPrimitive/isJsonObject/isJsonNull) before calling fromJson.
  3. Trace the producer of the JSON and fix it to emit the documented shapes only.

Example fix

// before
{"title": ["Hello", "Bonjour"]}

// after
{"title": {"en": "Hello", "fr": "Bonjour"}}
Defensive patterns

Strategy: type-guard

Validate before calling

if (el != null && !el.isJsonNull() && !el.isJsonPrimitive() && !el.isJsonObject()) throw new IllegalArgumentException("Unsupported element for LocalizedText");

Type guard

static boolean isLocalizedTextShape(@Nullable JsonElement el) { return el == null || el.isJsonNull() || el.isJsonPrimitive() || el.isJsonObject(); }

Try / catch

try { return LocalizedText.fromJson(el); } catch (JsonParseException e) { log.warn("Unexpected localized element", e); return null; }

Prevention

When it happens

Trigger: Calling LocalizedText.fromJson with a JsonArray (or any non-null, non-primitive, non-object element), e.g. a field containing ["a","b"].

Common situations: Upstream format change where localized fields became arrays; merging code that produces arrays; malformed hand-edited JSON.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/c40a874b963e4374. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/i18n/LocalizedText.java:78

                throw new JsonParseException("Localized text cannot be empty object");
            }

            var map = new LinkedHashMap<String, String>();
            jsonObject.asMap().forEach((k, v) -> {
                if (v instanceof JsonPrimitive primitive) {
                    map.put(k, primitive.getAsString());
                } else {
                    throw new JsonParseException("Localized text entry cannot be " + v.getClass());
                }
            });
            return new LocalizedText(map);
        }

        if (element instanceof JsonPrimitive primitive) {
            return new LocalizedText(primitive.getAsString());
        }

        throw new JsonParseException("Unexpected json element: " + element);
    }

    /// Reads a localized text value from a streaming JSON reader.
    ///
    /// The reader accepts JSON `null`, a JSON string, or an object whose values are strings.
    ///
    /// @param jsonReader the reader positioned at the next localized text value
    /// @return the parsed localized text, or `null` if the next token is JSON `null`
    /// @throws IOException         if reading from `jsonReader` fails
    /// @throws JsonSyntaxException if the next token is neither JSON `null`, a string, nor an object
    public static @Nullable LocalizedText read(JsonReader jsonReader) throws IOException {
        JsonToken nextToken = jsonReader.peek();
        if (nextToken == JsonToken.NULL) {
            jsonReader.nextNull();
            return null;
        } else if (nextToken == JsonToken.STRING) {
            return new LocalizedText(jsonReader.nextString());
        } else if (nextToken == JsonToken.BEGIN_OBJECT) {

View on GitHub (pinned to 24702dc5a0)