{"record":{"id":"fb07a195814c8b76","repo":"HMCL-dev/HMCL","slug":"theme-color-must-be-a-string-or-object","errorCode":null,"errorMessage":"Theme color must be a string or object","messagePattern":"Theme color must be a string or object","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java","lineNumber":73,"sourceCode":"    ///\n    /// @param element the JSON value\n    /// @return the parsed color source\n    /// @throws JsonParseException if the color source is malformed\n    static ThemeColorSource fromJson(JsonElement element) throws JsonParseException {\n        Objects.requireNonNull(element);\n        if (element instanceof JsonPrimitive primitive && primitive.isString()) {\n            String value = primitive.getAsString();\n            if (\"default\".equals(value.trim().replace('-', '_').toLowerCase(Locale.ROOT))) {\n                return DEFAULT;\n            }\n            @Nullable ThemeColor color = ThemeColor.of(value);\n            if (color == null) {\n                throw new JsonParseException(\"Invalid theme color: \" + value);\n            }\n            return custom(color);\n        }\n        if (!(element instanceof JsonObject object)) {\n            throw new JsonParseException(\"Theme color must be a string or object\");\n        }\n\n        JsonElement sourceElement = object.get(FIELD_SOURCE);\n        if (sourceElement == null) {\n            throw new JsonParseException(\"Theme color source is missing required field: \" + FIELD_SOURCE);\n        }\n        if (!(sourceElement instanceof JsonPrimitive sourcePrimitive) || !sourcePrimitive.isString()) {\n            throw new JsonParseException(\"Theme color source field must be a string: \" + FIELD_SOURCE);\n        }\n        String source = sourcePrimitive.getAsString();\n        String normalized = source.trim().replace('-', '_').toUpperCase(Locale.ROOT);\n        if (\"DEFAULT\".equals(normalized)) {\n            return DEFAULT;\n        }\n        if (\"WALLPAPER\".equals(normalized)) {\n            return wallpaper();\n        }\n        throw new JsonParseException(\"Unsupported theme color source: \" + source);","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java#L55-L91","documentation":"ThemeColorSource.fromJson throws this JsonParseException when the theme color JSON value is neither a string nor a JSON object — for example a number, boolean, array, or null. The parser supports exactly two shapes: a plain color-name string, or an object carrying a \"source\" field.","triggerScenarios":"fromJson on values like 42, true, [\"default\"], or [] where a color source is expected in the theme manifest.","commonSituations":"Programmatically generated manifests writing a numeric color code or a list of colors instead of a string/object; schema drift from another theme format.","solutions":["Change the value to a color-name string (e.g. \"default\" or a valid ThemeColor name)","Or use the object form: {\"source\": \"default\"} or {\"source\": \"wallpaper\"}","Fix the manifest generator to serialize the color source via ThemeColorSource.toJsonElement()"],"exampleFix":"// before\n\"color\": 16711680\n// after\n\"color\": \"default\"","handlingStrategy":"type-guard","validationCode":"if (!(element instanceof JsonPrimitive p && p.isString()) && !(element instanceof JsonObject)) throw new IllegalArgumentException(\"theme color must be a string or object\");","typeGuard":"boolean isColorSourceShape(JsonElement el) {\n    return (el instanceof JsonPrimitive p && p.isString()) || el instanceof JsonObject;\n}","tryCatchPattern":"try { ThemeColorSource.fromJson(el); } catch (JsonParseException e) { LOG.warning(\"Bad color shape: \" + e.getMessage()); src = ThemeColorSource.DEFAULT; }","preventionTips":["Serialize with ThemeColorSource.toJsonElement() to keep the shape correct","Use the plain string form for simple cases","Schema-validate generated manifests before shipping"],"tags":["json","type-mismatch","schema","theme-config"],"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"}