grpc/grpc-java · error · IllegalArgumentException

value '%s' for key '%s' is not a double

Error message

value '%s' for key '%s' is not a double

What it means

JsonUtil.getNumberAsDouble accepts JSON numbers and, as a convenience, numeric strings. When the value is a String that Double.parseDouble cannot parse, it throws IllegalArgumentException('value ... is not a double'). (Non-number, non-string types hit a sibling 'is not a number' error.)

Source

Thrown at core/src/main/java/io/grpc/internal/JsonUtil.java:116

  /**
   * Gets a number from an object for the given key.  If the key is not present, this returns null.
   * If the value does not represent a double, throws an exception.
   */
  @Nullable
  public static Double getNumberAsDouble(Map<String, ?> obj, String key) {
    assert key != null;
    if (!obj.containsKey(key)) {
      return null;
    }
    Object value = obj.get(key);
    if (value instanceof Double) {
      return (Double) value;
    }
    if (value instanceof String) {
      try {
        return Double.parseDouble((String) value);
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException(
            String.format("value '%s' for key '%s' is not a double", value, key));
      }
    }
    throw new IllegalArgumentException(
        String.format("value '%s' for key '%s' in '%s' is not a number", value, key, obj));
  }

  /**
   * Gets a number from an object for the given key, casted to an integer.  If the key is not
   * present, this returns null.  If the value does not represent an integer, throws an exception.
   */
  @Nullable
  public static Integer getNumberAsInteger(Map<String, ?> obj, String key) {
    assert key != null;
    if (!obj.containsKey(key)) {
      return null;
    }
    Object value = obj.get(key);

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Replace the string with a raw JSON number: "timeout": "30" -> "timeout": 30
  2. Strip units/symbols — gRPC durations with units belong in separate seconds/nanos fields, not in numeric values
  3. Normalize locale decimal separators (',' -> '.') if the config is machine-generated
  4. Validate numeric fields at config-load time before passing them to gRPC

Example fix

// before
{"timeoutSeconds": "30s"}
// after
{"timeoutSeconds": 30}
Defensive patterns

Strategy: validation

Validate before calling

Object v = cfg.get("timeoutSeconds");
if (v instanceof String && !((String) v).matches("-?\\d+(\\.\\d+)?")) throw new IllegalArgumentException("timeoutSeconds must be a plain number, no units");

Try / catch

try { double d = JsonUtil.getNumberAsDouble(cfg, "timeoutSeconds"); }
catch (IllegalArgumentException e) { if (e.getMessage().contains("is not a double")) { log.error("bad numeric field: {}", e.getMessage()); } throw e; }

Prevention

When it happens

Trigger: A config key expected to be numeric (timeout in seconds/nanos, percentages in xDS configs) is a String that is not parseable — e.g. "30s", "1.5x", empty string, or a localized number with comma decimal separator.

Common situations: Authors writing durations with units ("30s") in JSON fields that require bare numbers; spreadsheet-exported configs with locale-formatted decimals; template placeholders left unsubstituted ("${timeout}").

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/65e0db11c231da09. Report an issue: GitHub.