google/gson · error · NumberFormatException

Expected a long but was " + peekedString + locationString()

Error message

Expected a long but was " + peekedString + locationString()

What it means

Thrown by JsonReader.nextLong() as a NumberFormatException when the next token's value could be parsed as a double but loses precision when cast to long. This path is reached after the literal fails Long.parseLong but Double.parseDouble succeeds; the check `result != asDouble` detects the precision loss at JsonReader.java:1125.

Source

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

        // Fall back to parse as a double below.
      }
    } else {
      throw unexpectedTokenError("a long");
    }

    peeked = PEEKED_BUFFERED;
    double asDouble;
    try {
      asDouble = Double.parseDouble(peekedString);
    } catch (NumberFormatException e) {
      NumberFormatException rethrown =
          new NumberFormatException("Expected a long but was " + peekedString + locationString());
      rethrown.initCause(e);
      throw rethrown;
    }
    long result = (long) asDouble;
    if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
      throw new NumberFormatException("Expected a long but was " + peekedString + locationString());
    }
    peekedString = null;
    peeked = PEEKED_NONE;
    pathIndices[stackSize - 1]++;
    return result;
  }

  /**
   * Returns the string up to but not including {@code quote}, unescaping any character escape
   * sequences encountered along the way. The opening quote should have already been read. This
   * consumes the closing quote, but does not include it in the returned string.
   *
   * @param quote either ' or ".
   */
  private String nextQuotedValue(char quote) throws IOException {
    // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
    char[] buffer = this.buffer;
    StringBuilder builder = null;

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Use nextDouble() if the value can legitimately be fractional, then convert yourself.
  2. Fix the data source to emit integer literals representable as long.
  3. Use nextString() and parse with BigInteger/BigDecimal if values may exceed long range.
  4. Read with nextLong() only after confirming via peek() that the token is a NUMBER without a decimal/exponent.

Example fix

// before
long id = reader.nextLong(); // throws for "1.5" or 1e20

// after
double d = reader.nextDouble();
long id = (long) d; // or use BigDecimal for arbitrary precision
Defensive patterns

Strategy: validation

Validate before calling

// Check token shape before nextLong
if (reader.peek() == JsonToken.NUMBER) {
  // still may be fractional; read as string and test
  String s = reader.nextString();
  if (s.indexOf('.') >= 0 || s.indexOf('e') >= 0 || s.indexOf('E') >= 0) {
    // not an integer literal; handle accordingly
  } else {
    long v = Long.parseLong(s);
  }
}

Try / catch

try {
  return reader.nextLong();
} catch (NumberFormatException e) {
  // fall back to double or BigInteger parsing of the field
  return fallbackParseLong(reader);
}

Prevention

When it happens

Trigger: Calling nextLong() on a JSON value like 1.5, 1e10 (if not representable exactly as long), or a quoted string "12345678901234567890" whose double value does not equal its long truncation. The reader reads it as a double, casts to long, sees they differ, and throws.

Common situations: JSON sources that emit fractional numbers where the consumer expects integers; scientific-notation numbers; very large numeric IDs serialized as strings or doubles; schema mismatches between producer and consumer.

Related errors


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