HMCL-dev/HMCL · error · JsonParseException

Theme-pack manifest is missing themes array

Error message

Theme-pack manifest is missing themes array

What it means

Thrown by ThemePackManifest.readThemes when a manifest declares a "themes" member whose value is not a JSON array. The parser requires an array of theme objects when the multi-theme form is used; any other JSON type (object, string, number, boolean, null) is rejected so malformed packs fail fast at load time instead of producing a broken theme list.

Solutions

  1. Change "themes" to a JSON array: "themes": [ { ... }, { ... } ].
  2. If the pack has only one theme, use the singular form "theme": { ... } instead.
  3. Validate the manifest JSON structure (e.g. with a JSON schema or linter) before packaging.

Example fix

// before
"themes": {
  "dark": { "name": "Dark" }
}
// after
"themes": [
  { "id": "dark", "name": "Dark" }
]
Defensive patterns

Strategy: validation

Validate before calling

JsonElement themes = manifestJson.get("themes");
if (themes != null && !(themes instanceof JsonArray)) {
    throw new IllegalArgumentException("'themes' must be a JSON array");
}

Type guard

static boolean hasThemesArray(JsonObject manifest) {
    return manifest.get("themes") instanceof JsonArray;
}

Try / catch

try {
    ThemePackManifest pack = gson.fromJson(json, ThemePackManifest.class);
} catch (JsonParseException e) {
    // reject the pack and show a user-facing 'invalid manifest' message
}

Prevention

When it happens

Trigger: Deserializing a theme-pack manifest.json that contains "themes": { ... } (an object instead of an array), or "themes": "theme1", or "themes": null. Only reached when the manifest has a "themes" key but its value fails the `element instanceof JsonArray` check.

Common situations: Hand-edited manifest where the author wrapped themes in an object keyed by theme ID; a generator or script that serialized themes as a JSON object map; copy-paste of a single-theme pack and renaming "theme" to "themes" without converting the value to an array.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

    /// Reads the required theme declaration.
    private static List<Theme> readThemes(JsonObject object) {
        boolean hasSingleTheme = object.has("theme");
        boolean hasMultipleThemes = object.has("themes");
        if (hasSingleTheme == hasMultipleThemes) {
            throw new JsonParseException("Theme-pack manifest must declare exactly one of theme or themes");
        }

        if (hasSingleTheme) {
            JsonElement element = object.get("theme");
            if (!(element instanceof JsonObject themeObject)) {
                throw new JsonParseException("Theme-pack theme must be an object");
            }
            return List.of(Theme.fromJson(themeObject, false));
        }

        JsonElement element = object.get("themes");
        if (!(element instanceof JsonArray array)) {
            throw new JsonParseException("Theme-pack manifest is missing themes array");
        }
        if (array.isEmpty()) {
            throw new JsonParseException("Theme-pack themes array must declare at least one theme");
        }

        ArrayList<Theme> themes = new ArrayList<>(array.size());
        for (JsonElement item : array) {
            if (!(item instanceof JsonObject themeObject)) {
                throw new JsonParseException("Theme-pack theme must be an object");
            }
            themes.add(Theme.fromJson(themeObject, true));
        }
        return themes;
    }

    /// Checks that theme IDs and names are present whenever the manifest needs them for disambiguation.
    private static void checkThemeIdentities(List<Theme> themes) {
        if (themes.size() <= 1) {

View on GitHub (pinned to 24702dc5a0)