google/gson · error · NumberFormatException

Expected an int but was {}{}

Error message

Expected an int but was {}{}

What it means

Thrown by JsonReader.nextInt() (JsonReader.java:1334-1337) when the literal was recognized as a PEEKED_LONG (an integer literal that fit in a long) but does not fit in an int: (int) peekedLong != peekedLong. It is a NumberFormatException indicating an overflow when narrowing a valid long to int. The reader refuses to silently truncate.

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 310ac341f2)

Solutions

  1. Use nextLong() for fields that may exceed Integer.MAX_VALUE.
  2. Validate magnitude before reading, or guard with try/catch NumberFormatException and fall back to nextLong.
  3. Update the consuming field type from int to long.
  4. Check the JSON schema/producer for the field's numeric range.

Example fix

// before
int id = reader.nextInt(); // fails on 9007199254740993

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

Strategy: validation

Validate before calling

// If the value may exceed Integer.MAX_VALUE, read as long.
long v = reader.nextLong();
int narrowed = (int) v; // explicit, only if you accept truncation

Type guard

static boolean fitsInInt(long v) {
  return v >= Integer.MIN_VALUE && v <= Integer.MAX_VALUE;
}

Try / catch

try {
  return reader.nextInt();
} catch (NumberFormatException e) {
  return Math.toIntExact(reader.nextLong());
}

Prevention

When it happens

Trigger: Calling nextInt() on a JSON number larger than Integer.MAX_VALUE (2147483647) or smaller than Integer.MIN_VALUE, e.g. 9007199254740993 or a 64-bit id field read into an int.

Common situations: Reading a field that grew beyond int range over time (e.g. snowflake ids, timestamps in millis); schema mismatch where the producer emits 64-bit ids but the consumer calls nextInt; assuming a count fits in int.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/580e3850fefd529d. Report an issue: GitHub.