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
- Replace the string with a raw JSON number: "timeout": "30" -> "timeout": 30
- Strip units/symbols — gRPC durations with units belong in separate seconds/nanos fields, not in numeric values
- Normalize locale decimal separators (',' -> '.') if the config is machine-generated
- 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
- Never write units ("30s") into numeric JSON fields — use bare numbers
- Use the gRPC Duration shape ({seconds, nanos}) where durations are required
- Reject template placeholders like "${timeout}" before deploy
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
- Authorization policy should be a JSON object. Found: null
- value '%s' for key '%s' in '%s' is not List
- value '%s' for key '%s' in '%s' is not object
- value '%s' for key '%s' in '%s' is not a number
- Number expected to be integer:
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/65e0db11c231da09.
Report an issue: GitHub.