google/gson · error · NumberFormatException

Invalid number: " + value.substring(beginIndex, endIndex)

Error message

Invalid number: " + value.substring(beginIndex, endIndex)

What it means

ISO8601Utils.parseInt reports 'Invalid number' when the first character of an expected numeric field is not a digit (Character.digit returns < 0). This means a non-numeric character appears at the start of a year/month/day/hour/minute/second slot in the ISO-8601 string.

Source

Thrown at gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java:344

   * @param value the string to parse
   * @param beginIndex the start index for the integer in the string
   * @param endIndex the end index for the integer in the string
   * @return the int
   * @throws NumberFormatException if the value is not a number
   */
  private static int parseInt(String value, int beginIndex, int endIndex)
      throws NumberFormatException {
    if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {
      throw new NumberFormatException(value);
    }
    // use same logic as in Integer.parseInt() but less generic we're not supporting negative values
    int i = beginIndex;
    int result = 0;
    int digit;
    if (i < endIndex) {
      digit = Character.digit(value.charAt(i++), 10);
      if (digit < 0) {
        throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
      }
      result = -digit;
    }
    while (i < endIndex) {
      digit = Character.digit(value.charAt(i++), 10);
      if (digit < 0) {
        throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
      }
      result *= 10;
      result -= digit;
    }
    return -result;
  }

  /**
   * Zero pad a number to a specified length
   *
   * @param buffer buffer to use for padding

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Correct the source string so each numeric field begins with a digit.
  2. Pre-validate the format with an ISO-8601 regex before parsing.
  3. Register a custom date adapter with a lenient parser and logging.

Example fix

// before
Date d = gson.fromJson("\"20X4-01-01T00:00:00Z\"", Date.class);

// after
if (!source.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z")) throw new IllegalArgumentException("bad date");
Date d = gson.fromJson('"' + source + '"', Date.class);
Defensive patterns

Strategy: validation

Validate before calling

boolean isoDigitsAt(String iso, int start, int len) {
  if (iso == null || start + len > iso.length()) return false;
  for (int i = 0; i < len; i++) if (!Character.isDigit(iso.charAt(start + i))) return false;
  return true;
}

Type guard

static boolean firstFieldStartsWithDigit(String iso) {
  return iso != null && !iso.isEmpty() && Character.isDigit(iso.charAt(0));
}

Try / catch

try {
  Date d = gson.fromJson(json, Date.class);
} catch (JsonSyntaxException e) {
  // reject malformed date, log for producer follow-up
}

Prevention

When it happens

Trigger: A date string where a numeric component begins with a non-digit, e.g. '20X4-01-01' for the year, or a separator appears too early.

Common situations: Corrupted/tampered strings, encoding artifacts, or a producer that inserts a literal where a digit was expected.

Related errors


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