{"record":{"id":"68161ed42c18d9e1","repo":"HMCL-dev/HMCL","slug":"theme-pack-manifest-is-missing-themes-array","errorCode":null,"errorMessage":"Theme-pack manifest is missing themes array","messagePattern":"Theme-pack manifest is missing themes array","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java","lineNumber":158,"sourceCode":"    /// Reads the required theme declaration.\n    private static List<Theme> readThemes(JsonObject object) {\n        boolean hasSingleTheme = object.has(\"theme\");\n        boolean hasMultipleThemes = object.has(\"themes\");\n        if (hasSingleTheme == hasMultipleThemes) {\n            throw new JsonParseException(\"Theme-pack manifest must declare exactly one of theme or themes\");\n        }\n\n        if (hasSingleTheme) {\n            JsonElement element = object.get(\"theme\");\n            if (!(element instanceof JsonObject themeObject)) {\n                throw new JsonParseException(\"Theme-pack theme must be an object\");\n            }\n            return List.of(Theme.fromJson(themeObject, false));\n        }\n\n        JsonElement element = object.get(\"themes\");\n        if (!(element instanceof JsonArray array)) {\n            throw new JsonParseException(\"Theme-pack manifest is missing themes array\");\n        }\n        if (array.isEmpty()) {\n            throw new JsonParseException(\"Theme-pack themes array must declare at least one theme\");\n        }\n\n        ArrayList<Theme> themes = new ArrayList<>(array.size());\n        for (JsonElement item : array) {\n            if (!(item instanceof JsonObject themeObject)) {\n                throw new JsonParseException(\"Theme-pack theme must be an object\");\n            }\n            themes.add(Theme.fromJson(themeObject, true));\n        }\n        return themes;\n    }\n\n    /// Checks that theme IDs and names are present whenever the manifest needs them for disambiguation.\n    private static void checkThemeIdentities(List<Theme> themes) {\n        if (themes.size() <= 1) {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change \"themes\" to a JSON array: \"themes\": [ { ... }, { ... } ].","If the pack has only one theme, use the singular form \"theme\": { ... } instead.","Validate the manifest JSON structure (e.g. with a JSON schema or linter) before packaging."],"exampleFix":"// before\n\"themes\": {\n  \"dark\": { \"name\": \"Dark\" }\n}\n// after\n\"themes\": [\n  { \"id\": \"dark\", \"name\": \"Dark\" }\n]","handlingStrategy":"validation","validationCode":"JsonElement themes = manifestJson.get(\"themes\");\nif (themes != null && !(themes instanceof JsonArray)) {\n    throw new IllegalArgumentException(\"'themes' must be a JSON array\");\n}","typeGuard":"static boolean hasThemesArray(JsonObject manifest) {\n    return manifest.get(\"themes\") instanceof JsonArray;\n}","tryCatchPattern":"try {\n    ThemePackManifest pack = gson.fromJson(json, ThemePackManifest.class);\n} catch (JsonParseException e) {\n    // reject the pack and show a user-facing 'invalid manifest' message\n}","preventionTips":["Use the singular \"theme\" form only for single-theme packs","Round-trip your manifest through ThemePackManifest's serializer before release","Validate manifest JSON against the theme-pack schema in CI"],"tags":["json","theme-pack","manifest","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}