google/gson · error · NumberFormatException

Expected an int but was " + peekedLong + locationString()

Error message

Expected an int but was " + peekedLong + locationString()

What it means

Thrown by JsonReader.nextInt() as a NumberFormatException when the JSON literal was recognized as a complete integer (PEEKED_LONG) but its value does not fit in a Java int. The check `peekedLong != result` after casting to int detects the overflow at JsonReader.java:1335.

Source

Thrown at gson/src/main/java/com/google/gson/stream/JsonReader.java:1336

   * Returns the {@link JsonToken#NUMBER int} value of the next token, consuming it. If the next
   * token is a string, this method will attempt to parse it as an int. If the next token's numeric
   * value cannot be exactly represented by a Java {@code int}, this method throws.
   *
   * @throws IllegalStateException if the next token is neither a number nor a string.
   * @throws NumberFormatException if the next literal value cannot be parsed as a number, or
   *     exactly represented as an int.
   */
  public int nextInt() throws IOException {
    int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
    }

    int result;
    if (p == PEEKED_LONG) {
      result = (int) peekedLong;
      if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
        throw new NumberFormatException("Expected an int but was " + peekedLong + locationString());
      }
      peeked = PEEKED_NONE;
      pathIndices[stackSize - 1]++;
      return result;
    }

    if (p == PEEKED_NUMBER) {
      peekedString = new String(buffer, pos, peekedNumberLength);
      pos += peekedNumberLength;
    } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
      if (p == PEEKED_UNQUOTED) {
        peekedString = nextUnquotedValue();
      } else {
        peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
      }
      validateAscii(peekedString);
      try {
        result = Integer.parseInt(peekedString);

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Use nextLong() instead of nextInt() for values that may exceed Integer range.
  2. Ensure the data source emits values within int range, or switch the field type to long.
  3. Validate range before reading: peek() and consume as nextLong() then check bounds yourself.

Example fix

// before
int count = reader.nextInt(); // throws for 3000000000

// after
long count = reader.nextLong();
Defensive patterns

Strategy: validation

Validate before calling

// Read as long first, then range-check for int
long v = reader.nextLong();
if (v < Integer.MIN_VALUE || v > Integer.MAX_VALUE) {
  throw new ArithmeticException("Value " + v + " out of int range");
}
int i = (int) v;

Try / catch

try {
  return reader.nextInt();
} catch (NumberFormatException e) {
  // reposition or re-read as long and handle overflow
  throw e;
}

Prevention

When it happens

Trigger: Calling nextInt() on a JSON number larger than Integer.MAX_VALUE or smaller than Integer.MIN_VALUE, e.g. 3000000000 or -3000000000. The value fits in a long, but the narrowing cast to int changes it, so the precision check fails.

Common situations: 32-bit IDs or counters that have grown beyond Integer range; large counters/timestamps in milliseconds; producer using long IDs while consumer reads nextInt(); porting code from nextLong() to nextInt() without range checks.

Related errors


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