HMCL-dev/HMCL · error · JsonParseException
Localized text values must be strings
Error message
Localized text values must be strings: ${field} What it means
Thrown by parseLocalizedText when a localized text object contains a value that is not a JSON string. Each locale key in the localized object must map to a string; numbers, booleans, nested objects, arrays, or null values are rejected, with the field name in the message.
Solutions
- Quote every value in the localized object so all values are JSON strings.
- Remove or fix non-string entries; do not nest objects inside localized text.
- Add a lint step that checks localized maps for string-only values.
Example fix
// before
"name": { "en": "My Theme", "zh": 123 }
// after
"name": { "en": "My Theme", "zh": "我的主题" } Defensive patterns
Strategy: validation
Validate before calling
JsonObject name = manifestJson.getAsJsonObject("name");
for (Map.Entry<String, JsonElement> e : name.entrySet()) {
if (!(e.getValue() instanceof JsonPrimitive p && p.isString())) {
throw new IllegalArgumentException("locale '" + e.getKey() + "' must map to a string");
}
} Type guard
static boolean isStringValuedMap(JsonObject localized) {
for (JsonElement v : localized.values()) {
if (!(v instanceof JsonPrimitive p) || !p.isString()) return false;
}
return true;
} Try / catch
try {
ThemePackManifest pack = gson.fromJson(json, ThemePackManifest.class);
} catch (JsonParseException e) {
// highlight the localized field named in the message
} Prevention
- Configure translation tools to export all values as strings
- Avoid nesting objects inside localized text maps
- Lint localized maps for string-only values in CI
When it happens
Trigger: Deserializing a manifest where a localized object has a non-string value, e.g. "name": { "en": "My Theme", "zh": 123 } or "description": { "en": { "text": "x" } }.
Common situations: A translation tool exported counts or flags as numbers; an author accidentally nested locale maps; a script interpolated a numeric variable as the translation value.
Related errors
- Theme-pack localized text must be a string or object
- Theme-pack theme must be an object
- Theme-pack manifest field must be a string
- Localized text field is empty
- Localized text entry cannot be
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/82fccfd6acaec33c.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java:216
}
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()) {
throw new JsonParseException("Localized text values must be strings: " + field);
}
localizedValues.put(
requireNonBlank(entry.getKey(), field),
requireNonBlank(primitive.getAsString(), field));
}
return new LocalizedText(localizedValues);
}
throw new JsonParseException("Theme-pack localized text must be a string or object: " + field);
}
/// Returns a validated localized text value.
static LocalizedText requireLocalizedText(LocalizedText value, String field) {
Objects.requireNonNull(value);
JsonElement element = JsonUtils.GSON.toJsonTree(value, LocalizedText.class);
return parseLocalizedText(element, field);
}
View on GitHub (pinned to 24702dc5a0)