{"id":"73a9248482ccae78","repo":"google/gson","slug":"invalid-number-value-substring-beginindex-en","errorCode":null,"errorMessage":"Invalid number: \" + value.substring(beginIndex, endIndex)","messagePattern":"Invalid number: \" \\+ value\\.substring\\(beginIndex, endIndex\\)","errorType":"exception","errorClass":"NumberFormatException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java","lineNumber":344,"sourceCode":"   * @param value the string to parse\n   * @param beginIndex the start index for the integer in the string\n   * @param endIndex the end index for the integer in the string\n   * @return the int\n   * @throws NumberFormatException if the value is not a number\n   */\n  private static int parseInt(String value, int beginIndex, int endIndex)\n      throws NumberFormatException {\n    if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {\n      throw new NumberFormatException(value);\n    }\n    // use same logic as in Integer.parseInt() but less generic we're not supporting negative values\n    int i = beginIndex;\n    int result = 0;\n    int digit;\n    if (i < endIndex) {\n      digit = Character.digit(value.charAt(i++), 10);\n      if (digit < 0) {\n        throw new NumberFormatException(\"Invalid number: \" + value.substring(beginIndex, endIndex));\n      }\n      result = -digit;\n    }\n    while (i < endIndex) {\n      digit = Character.digit(value.charAt(i++), 10);\n      if (digit < 0) {\n        throw new NumberFormatException(\"Invalid number: \" + value.substring(beginIndex, endIndex));\n      }\n      result *= 10;\n      result -= digit;\n    }\n    return -result;\n  }\n\n  /**\n   * Zero pad a number to a specified length\n   *\n   * @param buffer buffer to use for padding","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java#L326-L362","documentation":"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.","triggerScenarios":"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.","commonSituations":"Corrupted/tampered strings, encoding artifacts, or a producer that inserts a literal where a digit was expected.","solutions":["Correct the source string so each numeric field begins with a digit.","Pre-validate the format with an ISO-8601 regex before parsing.","Register a custom date adapter with a lenient parser and logging."],"exampleFix":"// before\nDate d = gson.fromJson(\"\\\"20X4-01-01T00:00:00Z\\\"\", Date.class);\n\n// after\nif (!source.matches(\"\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}Z\")) throw new IllegalArgumentException(\"bad date\");\nDate d = gson.fromJson('\"' + source + '\"', Date.class);","handlingStrategy":"validation","validationCode":"boolean isoDigitsAt(String iso, int start, int len) {\n  if (iso == null || start + len > iso.length()) return false;\n  for (int i = 0; i < len; i++) if (!Character.isDigit(iso.charAt(start + i))) return false;\n  return true;\n}","typeGuard":"static boolean firstFieldStartsWithDigit(String iso) {\n  return iso != null && !iso.isEmpty() && Character.isDigit(iso.charAt(0));\n}","tryCatchPattern":"try {\n  Date d = gson.fromJson(json, Date.class);\n} catch (JsonSyntaxException e) {\n  // reject malformed date, log for producer follow-up\n}","preventionTips":["Validate ISO-8601 against a strict regex before parsing.","Reject unexpected characters at known offsets.","Use java.time DateTimeFormatter with strictResolverStyle."],"tags":["gson","date-parsing","iso8601","json"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}