HMCL-dev/HMCL · error · JsonParseException

Theme-pack manifest is not a JsonObject

Error message

Theme-pack manifest is not a JsonObject

What it means

The ThemePackManifest Gson Adapter.deserialize throws JsonParseException when the top-level manifest JSON is present but not a JsonObject (e.g. an array, string, or number). A theme-pack manifest must be a JSON object with id/version/name members. null/JsonNull yields null instead of this error.

Solutions

  1. Ensure theme_pack.json starts with '{' and ends with '}' wrapping all members.
  2. Validate the file with a JSON parser/linter to confirm the root type is an object.
  3. Re-extract or re-download the theme pack if the file inside the zip is damaged or wrong.

Example fix

// before
[ { "id": "x", ... } ]
// after
{ "id": "x", ... }
Defensive patterns

Strategy: try-catch

Validate before calling

JsonElement root = JsonParser.parseString(text); if (!root.isJsonObject()) throw new IllegalArgumentException("manifest root must be an object");

Type guard

if (element instanceof JsonObject obj) { /* safe to parse */ }

Try / catch

try { return adapter.deserialize(json, type, ctx); } catch (JsonParseException e) { return Optional.empty(); }

Prevention

When it happens

Trigger: Feeding a manifest file whose root is a JSON array or bare string into the Gson deserializer via the Adapter; a truncated or rewritten manifest that lost its outer braces.

Common situations: Concatenating or splitting JSON files corrupted the root; a build step emitted a list of manifests instead of one object; the zip contains the wrong file at the manifest path.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java:274

        String id = requireNonBlank(value, field);
        if (!PACKAGE_ID_PATTERN.matcher(id).matches()) {
            throw new IllegalArgumentException(messagePrefix + value);
        }
        return id;
    }

    static final class Adapter implements JsonSerializer<@Nullable ThemePackManifest>,
            JsonDeserializer<@Nullable ThemePackManifest> {

        @Override
        public @Nullable ThemePackManifest deserialize(
                @Nullable JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            if (json == null || json instanceof JsonNull) {
                return null;
            }

            if (!(json instanceof JsonObject object)) {
                throw new JsonParseException("Theme-pack manifest is not a JsonObject");
            }

            if (!JsonSchema.check(object, CURRENT_SCHEMA).readable()) {
                throw new JsonParseException("Unsupported theme-pack schema: " + object.get(JsonSchema.PROPERTY_SCHEMA));
            }

            String id = requireMemberString(object, "id");
            String version = requireMemberString(object, "version");

            LocalizedText name = context.deserialize(object.get("name"), LocalizedText.class);
            if (name == null) {
                throw new JsonParseException("Theme-pack manifest is missing name");
            }

            @Nullable LocalizedText description;
            try {
                description = context.deserialize(object.get("description"), LocalizedText.class);
                if (description != null) {

View on GitHub (pinned to 24702dc5a0)