HMCL-dev/HMCL · error · JsonParseException
Theme-pack manifest field must be a string
Error message
Theme-pack manifest field must be a string: ${fieldName} What it means
Thrown by requireMemberString when a required manifest member exists but is not a JSON string primitive. Fields such as "id" and "version" must be plain strings; objects, arrays, numbers, booleans, or null values are rejected with the offending field name in the message.
Solutions
- Quote the value so it is a JSON string: "version": "1.0.0" instead of 1.0.
- Flatten any nested object into the expected string form.
- Validate the manifest with a JSON schema checker before packaging.
Example fix
// before "version": 1.0 // after "version": "1.0.0"
Defensive patterns
Strategy: validation
Validate before calling
for (String field : new String[]{"id", "version"}) {
JsonElement el = manifestJson.get(field);
if (el != null && !(el instanceof JsonPrimitive p && p.isString())) {
throw new IllegalArgumentException(field + " must be a JSON string");
}
} Type guard
static boolean isStringField(JsonObject manifest, String field) {
return manifest.get(field) instanceof JsonPrimitive p && p.isString();
} Try / catch
try {
ThemePackManifest pack = gson.fromJson(json, ThemePackManifest.class);
} catch (JsonParseException e) {
// point the author at the offending field named in the message
} Prevention
- Quote version numbers and IDs in JSON — never emit them as numbers
- Avoid null placeholders; omit optional fields entirely instead
- Run a JSON schema validator that checks types, not just presence
When it happens
Trigger: Deserializing a manifest with "id": 123, "version": { "major": 1 }, or "id": null — any case where `object.get(fieldName)` returns a non-string JsonPrimitive or non-primitive.
Common situations: Author wrote a version as a number (1.0 instead of "1.0"); nested metadata objects from another tool's format pasted into the manifest; a placeholder null left in place of a value.
Related errors
- Theme-pack theme must be an object
- Theme-pack author must be an object or a string
- Missing author name:
- Manifest is null
- Invalid theme-pack manifest
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/f87e25d5f271e289.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java:197
for (Theme theme : themes) {
if (theme.id() == null) {
throw new IllegalArgumentException("Theme ID is required when a theme pack declares multiple themes");
}
if (theme.name() == null) {
throw new IllegalArgumentException("Theme name is required when a theme pack declares multiple themes");
}
}
}
/// Reads a required string member.
private static String requireMemberString(JsonObject object, String fieldName) {
JsonElement element = object.get(fieldName);
if (element == null) {
throw new JsonParseException("Theme-pack manifest is missing " + fieldName);
}
if (!(element instanceof JsonPrimitive primitive) || !primitive.isString()) {
throw new JsonParseException("Theme-pack manifest field must be a string: " + fieldName);
}
return requireNonBlank(primitive.getAsString(), fieldName);
}
/// Parses a localized text value.
static LocalizedText parseLocalizedText(JsonElement element, String field) {
if (element instanceof JsonPrimitive primitive && primitive.isString()) {
return LocalizedText.plain(requireNonBlank(primitive.getAsString(), field));
}
if (element instanceof JsonObject localizedObject) {
if (localizedObject.isEmpty()) {
throw new JsonParseException("Localized text field is empty: " + field);
}
LinkedHashMap<String, String> localizedValues = new LinkedHashMap<>();
for (Map.Entry<String, JsonElement> entry : localizedObject.entrySet()) {
JsonElement value = entry.getValue();
if (!(value instanceof JsonPrimitive primitive) || !primitive.isString()) {View on GitHub (pinned to 24702dc5a0)