HMCL-dev/HMCL · error · JsonParseException
The instant should be a string value
Error message
The instant should be a string value
What it means
InstantTypeAdapter expects the incoming JSON element to be a JSON string (a primitive) that it can parse into a timestamp. This JsonParseException is thrown when the element is an object, array, number, boolean, or null instead of a string.
Solutions
- Serialize the Instant as a string (InstantTypeAdapter does this via toString/ISO format).
- Convert numeric epoch values to a string or to Instant before deserialization.
- If null is possible, declare the field as @Nullable Instant or use a null-safe TypeAdapter.
- Preprocess the JsonElement to a primitive string before handing it to Gson.
Example fix
// before
{"lastLogin": 1720000000000}
// after
{"lastLogin": "2024-07-03T09:06:40Z"} Defensive patterns
Strategy: validation
Validate before calling
JsonElement el = JsonParser.parseString(json);
if (!(el instanceof JsonPrimitive p) || !p.isString())
throw new IllegalArgumentException("Instant field must be a JSON string"); Type guard
static boolean isJsonStringInstant(JsonElement el) {
return el instanceof JsonPrimitive p && p.isString();
} Try / catch
try {
return gson.fromJson(json, Instant.class);
} catch (JsonParseException e) {
log.warn("Instant field was not a JSON string", e);
} Prevention
- Serialize Instants with this adapter so they are written as strings.
- Convert epoch-millis timestamps to ISO strings before deserializing.
- Make Instant fields nullable explicitly rather than sending JSON null.
When it happens
Trigger: Deserializing JSON where an Instant-typed field holds a number (epoch millis), a nested object, or null instead of a formatted date-time string.
Common situations: APIs or files that serialize timestamps as epoch milliseconds or ISO objects; JSON hand-edited to "lastLogin": 1720000000000 instead of a quoted string.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- json.toString()
- PortablePath must be a string: " + in.peek()
- Config is not an object:
- json.toString()
- e
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/7094bc6b8d3c4198.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/InstantTypeAdapter.java:75
/// Deserializes a JSON string into an [Instant].
///
/// @param json the JSON element to deserialize
/// @param type the requested target type
/// @param context the Gson deserialization context
/// @return the parsed instant
/// @throws JsonParseException if `json` is not a string or cannot be parsed as an instant
/// @throws IllegalArgumentException if `type` is not [Instant]
@Override
public Instant deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
if (json instanceof JsonPrimitive) {
Instant time = deserializeToInstant(json.getAsString());
if (type == Instant.class)
return time;
else
throw new IllegalArgumentException(this.getClass() + " cannot be deserialized to " + type);
} else {
throw new JsonParseException("The instant should be a string value");
}
}
/// Formatter for the legacy US localized date-time representation.
private static final DateTimeFormatter EN_US_FORMAT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM)
.withLocale(Locale.US)
.withZone(ZoneId.systemDefault());
/// Formatter for ISO local date-time text followed by one of the supported offset forms.
private static final DateTimeFormatter ISO_DATE_TIME = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
.optionalStart().appendOffset("+H:MM", "+0:00").optionalEnd()
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
.optionalStart().appendOffsetId().optionalEnd()
.toFormatter();
View on GitHub (pinned to 24702dc5a0)