{"record":{"id":"a41116b7753526ae","repo":"HMCL-dev/HMCL","slug":"theme-color-source-is-missing-required-field","errorCode":null,"errorMessage":"Theme color source is missing required field: ","messagePattern":"Theme color source is missing required field: ","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java","lineNumber":78,"sourceCode":"        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);\n    }\n\n    /// Converts this color source to its JSON representation.\n    ///\n    /// @return the color source JSON value","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java#L60-L96","documentation":"ThemeColorSource.fromJson throws this JsonParseException when the theme color is provided as an object but the required \"source\" member is absent. The object form exists solely to carry the source discriminator (\"default\" or \"wallpaper\"), so an object without it cannot be classified.","triggerScenarios":"fromJson on {\"seed\":\"red\"}, {}, or {\"color\":\"default\"} — any JsonObject without a \"source\" key in the color-source position of a theme manifest.","commonSituations":"Theme authors nesting extra color options in the object while forgetting the \"source\" key, or hand-writing the object form instead of the simpler string form.","solutions":["Add the \"source\" field: {\"source\": \"default\"} or {\"source\": \"wallpaper\"}","Alternatively use the plain string form \"default\" instead of the object form","Serialize sources programmatically via ThemeColorSource.toJsonElement() to guarantee the field"],"exampleFix":"// before\n\"color\": {}\n// after\n\"color\": {\"source\": \"wallpaper\"}","handlingStrategy":"validation","validationCode":"if (element instanceof JsonObject o && !o.has(\"source\")) throw new IllegalArgumentException(\"color source object requires \\\"source\\\" field\");","typeGuard":"boolean hasSourceField(JsonElement el) {\n    return !(el instanceof JsonObject o) || o.has(\"source\");\n}","tryCatchPattern":"try { ThemeColorSource.fromJson(el); } catch (JsonParseException e) { LOG.warning(\"Missing source field: \" + e.getMessage()); src = ThemeColorSource.DEFAULT; }","preventionTips":["Always include \"source\" in the object form","Prefer the string form \"default\" to avoid the object shape entirely","Build sources via the Default/Custom/Wallpaper records and toJsonElement()"],"tags":["json","missing-field","theme-config"],"backgroundTag":"missing-required-config-field","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"}