HMCL-dev/HMCL · error · JsonParseException

Theme color source field must be a string:

Error message

Theme color source field must be a string: 

What it means

ThemeColorSource.fromJson throws this JsonParseException when the color source object's "source" member exists but is not a JSON string primitive — e.g. a number, boolean, nested object, or array. The source discriminator must be the literal string "default" or "wallpaper" (case-insensitive, '-' and '_' interchangeable).

Solutions

  1. Make "source" a string: {"source": "default"} or {"source": "wallpaper"}
  2. Fix generators to write the enum name string, not an ordinal or object
  3. Validate the manifest with a JSON schema before loading

Example fix

// before
"color": {"source": 1}
// after
"color": {"source": "wallpaper"}
Defensive patterns

Strategy: type-guard

Validate before calling

if (element instanceof JsonObject o && o.has("source") && !(o.get("source") instanceof JsonPrimitive p && p.isString())) throw new IllegalArgumentException("\"source\" must be a string");

Type guard

boolean hasStringSource(JsonElement el) {
    return !(el instanceof JsonObject o) || (o.get("source") instanceof JsonPrimitive p && p.isString());
}

Try / catch

try { ThemeColorSource.fromJson(el); } catch (JsonParseException e) { LOG.warning("Bad source field: " + e.getMessage()); src = ThemeColorSource.DEFAULT; }

Prevention

When it happens

Trigger: fromJson on {"source": 1}, {"source": true}, {"source": ["default"]}, or {"source": {"name":"default"}} in the theme manifest's color-source position.

Common situations: Generated manifests that emit the source as a numeric enum ordinal or a nested config object instead of its string name; YAML/JSON authoring mistakes.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java:81

            if ("default".equals(value.trim().replace('-', '_').toLowerCase(Locale.ROOT))) {
                return DEFAULT;
            }
            @Nullable ThemeColor color = ThemeColor.of(value);
            if (color == null) {
                throw new JsonParseException("Invalid theme color: " + value);
            }
            return custom(color);
        }
        if (!(element instanceof JsonObject object)) {
            throw new JsonParseException("Theme color must be a string or object");
        }

        JsonElement sourceElement = object.get(FIELD_SOURCE);
        if (sourceElement == null) {
            throw new JsonParseException("Theme color source is missing required field: " + FIELD_SOURCE);
        }
        if (!(sourceElement instanceof JsonPrimitive sourcePrimitive) || !sourcePrimitive.isString()) {
            throw new JsonParseException("Theme color source field must be a string: " + FIELD_SOURCE);
        }
        String source = sourcePrimitive.getAsString();
        String normalized = source.trim().replace('-', '_').toUpperCase(Locale.ROOT);
        if ("DEFAULT".equals(normalized)) {
            return DEFAULT;
        }
        if ("WALLPAPER".equals(normalized)) {
            return wallpaper();
        }
        throw new JsonParseException("Unsupported theme color source: " + source);
    }

    /// Converts this color source to its JSON representation.
    ///
    /// @return the color source JSON value
    JsonElement toJsonElement();

    /// Returns the best available color without accessing wallpaper pixels.

View on GitHub (pinned to 24702dc5a0)