google/gson · error · IllegalStateException

Expected NUMBER but was ${token} at path ${path}

Error message

Expected NUMBER but was ${token} at path ${path}

What it means

JsonTreeReader.nextDouble accepts NUMBER or (in lenient mode) STRING tokens; any other token throws IllegalStateException. Called by adapters deserializing double/Double fields from a JsonElement tree.

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

Solutions

  1. Ensure the JSON value for the field is a JSON number.
  2. Register a custom TypeAdapter that coerces or rejects gracefully.
  3. Make the adapter nullSafe if nulls are possible.
  4. Add contract tests asserting numeric types for the field.

Example fix

// before: {"amount":null} into non-nullSafe double
Gson().fromJson("{\"amount\":null}", Price::class.java) // Expected NUMBER

// after: {"amount":0.0}
Gson().fromJson("{\"amount\":0.0}", Price::class.java)
Defensive patterns

Strategy: validation

Validate before calling

// Validate token type before nextDouble
JsonToken t = reader.peek();
if (t == NUMBER || t == STRING) {
  double d = reader.nextDouble();
} else {
  // missing or wrong type
  reader.skipValue();
}

Try / catch

try {
  reader.nextDouble();
} catch (IllegalStateException e) {
  if (e.getMessage().startsWith("Expected NUMBER")) {
    reader.skipValue(); // tolerate wrong type
  } else throw e;
}

Prevention

When it happens

Trigger: Deserializing a double field whose JSON value is a boolean, null, object, or array, when the source is a JsonElement tree (JsonTreeReader).

Common situations: Schema drift where a numeric field starts receiving booleans/objects; null literal reaching a non-nullSafe double adapter.

Related errors


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