google/gson · error · IllegalStateException
Expected NUMBER but was {token}{location}
Error message
Expected NUMBER but was {token}{location} What it means
JsonTreeReader.nextDouble throws IllegalStateException('Expected NUMBER but was ' + token + locationString) when the current token is neither NUMBER nor STRING. nextDouble accepts STRING as a convenience (it parses the string to double), but rejects BOOLEAN, NULL, NAME, BEGIN_*, END_*. The {token} is the actual JsonToken, {location} the path.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java:243
pathIndices[stackSize - 1]++;
}
return result;
}
@Override
public void nextNull() throws IOException {
expect(JsonToken.NULL);
popStack();
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
}
@Override
public double nextDouble() throws IOException {
JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
throw new IllegalStateException(
"Expected " + JsonToken.NUMBER + " but was " + token + locationString());
}
JsonPrimitive primitive = (JsonPrimitive) peekStack();
double result;
try {
result = primitive.getAsDouble();
} catch (NumberFormatException e) {
throw numberFormatException("Expected a double but was " + primitive.getAsString(), e);
}
if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new MalformedJsonException("JSON forbids NaN and infinities: " + result);
}
popStack();
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
return result;
}View on GitHub (pinned to 8b8628c656)
Solutions
- Branch on in.peek() before nextDouble(): handle NULL with nextNull(), handle STRING with explicit parse if allowed, reject other tokens.
- If the field can legitimately be null, make the Java field a boxed Double and read null explicitly.
- Validate the JSON shape before deserializing, or use a JsonElement capture +getAsDouble() with your own error handling.
- Fix the producer to emit the numeric value consistently.
Example fix
// before
double v = in.nextDouble(); // throws if token is NULL
// after
Double v;
if (in.peek() == JsonToken.NULL) { in.nextNull(); v = null; }
else { v = in.nextDouble(); } Defensive patterns
Strategy: type-guard
Validate before calling
JsonToken t = reader.peek();
if (t != JsonToken.NUMBER && t != JsonToken.STRING) {
throw new IllegalStateException("Expected number at " + reader.getPath() + ", got " + t);
}
double v = reader.nextDouble(); Type guard
static boolean isNumberOrStringToken(JsonReader r) throws IOException {
JsonToken t = r.peek();
return t == JsonToken.NUMBER || t == JsonToken.STRING;
} Try / catch
try {
return reader.nextDouble();
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Expected NUMBER")) {
throw new InvalidPayloadException("Expected numeric field at " + reader.getPath(), e);
}
throw e;
} Prevention
- Handle NULL explicitly when numeric fields are nullable.
- Use boxed Double for optional numeric fields.
- Validate the JSON shape with a schema when strict typing matters.
When it happens
Trigger: Calling nextDouble() when the reader sits on a boolean, null, object, or array. Common when deserializing a numeric field whose value is missing (NULL) or accidentally encoded as a nested object.
Common situations: Numeric field changed to nullable in a new API version (null instead of 0); field swapped from number to boolean flag; wrong key being read so the cursor lands on a structural token.
Related errors
- Expected {expected} but was {peek}{location}
- Expected STRING but was {token}{location}
- JSON forbids NaN and infinities: {result}
- JsonReader is closed
- GSON ({GsonBuildConfig.VERSION}) cannot handle {type}
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/1c4cfb8b100e2b24.json.
Report an issue: GitHub.