google/gson · error · ParseException
Failed to parse date
Error message
Failed to parse date [${input}]: ${fail.getMessage()} What it means
Thrown by UtcDateTypeAdapter when an ISO-8601 date string cannot be parsed. The adapter ported Jackson's ISO8601Utils parser; if any extraction step throws IndexOutOfBoundsException or IllegalArgumentException (truncated digits, missing timezone indicator, invalid chars), it captures that cause and rethrows a ParseException whose message embeds the offending input and the underlying reason. The read() method wraps this ParseException into a JsonParseException.
Solutions
- Send dates as strict ISO-8601: yyyy-MM-dd'T'HH:mm:ss[.sss][Z|+HH:mm], e.g. "2024-05-01T12:30:00Z".
- If your input uses a different format, register a custom TypeAdapter<Date> built from a matching SimpleDateFormat instead of UtcDateTypeAdapter.
- Pre-validate the string with a regex / DateTimeFormatter.parse() before deserialization to give a clearer error.
- Log the exact input value from the message to identify the offending producer.
Example fix
// before: input "2020-13-40" -> throws String json = "\"2020-13-40\""; // after: valid ISO-8601 UTC String json = "\"2020-05-01T12:30:00Z\"";
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate ISO-8601 before deserialization
static boolean isIso8601(String s) {
return s != null && s.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:?\\d{2})");
}
if (!isIso8601(dateStr)) throw new IllegalArgumentException("Bad date: " + dateStr); Try / catch
try { gson.fromJson(json, Date.class); }
catch (JsonParseException e) {
Throwable c = e.getCause();
if (c instanceof ParseException && c.getMessage().startsWith("Failed to parse date")) {
// log offending value, apply fallback parsing or reject
} else throw e;
} Prevention
- Standardize on ISO-8601 UTC across producers and consumers.
- If formats differ, register a custom TypeAdapter<Date> using the matching SimpleDateFormat.
- Capture and log the exact input value to diagnose the producer.
When it happens
Trigger: Date string not in ISO-8601 form (e.g. "01/02/2020", epoch millis, "Mar 5, 2021"); string too short for the year/month/day digits; missing or invalid timezone indicator (not 'Z', '+', or '-'); non-numeric characters where digits expected; null handled separately but malformed values reach parse().
Common situations: Mixing date formats between producer and consumer; backend sending locale-specific formats; receiving epoch numbers as strings; trimming bugs that drop the timezone; APIs that emit RFC1123 or custom formats instead of ISO-8601.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed parsing ' ' as Date; at path
- Array must have size 1, but has size
- Attempted to deserialize a java.lang.Class. Forgot to…
- Cannot allocate . Usage of JDK sun.misc.Unsafe is enabled…
- cannot deserialize because it does not define a field named
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/668e8780f49fc047.
Report an issue: GitHub.
Appendix: source
Thrown at extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java:230
Calendar calendar = new GregorianCalendar(timezone);
calendar.setLenient(false);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minutes);
calendar.set(Calendar.SECOND, seconds);
calendar.set(Calendar.MILLISECOND, milliseconds);
pos.setIndex(offset);
return calendar.getTime();
// If we get a ParseException it'll already have the right message/offset.
// Other exception types can convert here.
} catch (IndexOutOfBoundsException | IllegalArgumentException e) {
fail = e;
}
String input = (date == null) ? null : ("'" + date + "'");
throw new ParseException(
"Failed to parse date [" + input + "]: " + fail.getMessage(), pos.getIndex());
}
/**
* Check if the expected character exist at the given offset in the value.
*
* @param value the string to check at the specified offset
* @param offset the offset to look for the expected character
* @param expected the expected character
* @return true if the expected character exist at the given offset
*/
private static boolean checkOffset(String value, int offset, char expected) {
return (offset < value.length()) && (value.charAt(offset) == expected);
}
/**
* Parse an integer located between 2 given offsets in a string
*View on GitHub (pinned to 310ac341f2)