google/gson · error · ParseException

Failed to parse date [{input}]: {fail.getMessage()}

Error message

Failed to parse date [{input}]: {fail.getMessage()}

What it means

The terminal ParseException thrown by UtcDateTypeAdapter.parse() after any inner IndexOutOfBoundsException or IllegalArgumentException (the 'No time zone indicator', 'Invalid time zone indicator', or NumberFormatException cases) is caught. It bundles the original failure message and the input string for diagnostics. read() catches this ParseException and rethrows it as a JsonParseException, so callers see JsonParseException (a JsonParseException is-a RuntimeException).

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 8b8628c656)

Solutions

  1. Read the inner fail.getMessage() to identify the specific cause (No time zone indicator / Invalid time zone indicator / Invalid number), then apply the matching fix for errors 5, 6, 9, or 10.
  2. Log the raw input string shown in the message and correct it at the source.
  3. If you cannot control the input format, replace UtcDateTypeAdapter with a custom adapter using SimpleDateFormat or java.time with the actual format.
  4. Add input validation (regex or try java.time.parse) before deserialization.

Example fix

// before: input not valid ISO-8601
String json = "\"01/15/2024 12:00\"";
Date d = gson.fromJson(json, Date.class); // JsonParseException: Failed to parse date [...]

// after: provide strict ISO-8601, or use a matching format adapter
Gson gson = new GsonBuilder().setDateFormat("MM/dd/yyyy HH:mm").create();
Date d = gson.fromJson(json, Date.class);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate ISO-8601 shape before handing to the adapter
static boolean isIso8601(String s) {
  if (s == null) return false;
  // strict-ish regex: date [T time] [zone]
  return s.matches("^\\d{4}-?\\d{2}-?\\d{2}(T\\d{2}:?\\d{2}(:?\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$");
}

Type guard

static boolean isParsableUtcDate(String s) {
  try { return isIso8601(s); } catch (Exception e) { return false; }
}

Try / catch

try {
  Date d = gson.fromJson(json, Date.class);
} catch (JsonParseException e) {
  // Inspect e.getCause().getMessage() to classify: no-zone / bad-indicator / bad-number
  logger.warn("Unparsable date in payload: {}", json, e);
  // optionally fall back to a lenient adapter or reject the record
}

Prevention

When it happens

Trigger: Any malformed ISO-8601 date string reaching UtcDateTypeAdapter: too short, non-numeric digits where year/month/day/hour/min/sec are expected, missing or invalid timezone indicator, out-of-bounds substrings. This is the umbrella message for all date-parse failures in this adapter.

Common situations: Integrating with systems whose date format is not strict ISO-8601; null-ish or empty date strings; locale-formatted dates; timestamps from databases in non-UTC columns; corrupted payloads.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/cfbffe6de1ea6264.json. Report an issue: GitHub.