grpc/grpc-java · error · ClassCastException
Number expected to be long:
Error message
Number expected to be long:
What it means
getNumberAsLong casts a Double config value to long. If the fractional part is non-zero (or the double is outside exact long representation), the cast would lose data, so a ClassCastException with 'Number expected to be long: ' is thrown. It guards long-typed channel/service-config fields.
Source
Thrown at core/src/main/java/io/grpc/internal/JsonUtil.java:170
String.format("value '%s' for key '%s' is not an integer", value, key));
}
/**
* Gets a number from an object for the given key, casted to an long. If the key is not
* present, this returns null. If the value does not represent a long integer, throws an
* exception.
*/
public static Long getNumberAsLong(Map<String, ?> obj, String key) {
assert key != null;
if (!obj.containsKey(key)) {
return null;
}
Object value = obj.get(key);
if (value instanceof Double) {
Double d = (Double) value;
long l = d.longValue();
if (l != d) {
throw new ClassCastException("Number expected to be long: " + d);
}
return l;
}
if (value instanceof String) {
try {
return Long.parseLong((String) value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
String.format("value '%s' for key '%s' is not a long integer", value, key));
}
}
throw new IllegalArgumentException(
String.format("value '%s' for key '%s' is not a long integer", value, key));
}
/**
* Gets a string from an object for the given key. If the key is not present, this returns null.
* If the value is not a String, throws an exception.View on GitHub (pinned to 64daddc1f3)
Solutions
- Make the config value an exact whole number (1000.5 -> 1000) or compute nanos with integer arithmetic upstream.
- If the value exceeds long range, reduce the unit (use millis/micros) or change the field semantics.
- Catch ClassCastException around the call and log the offending key for config debugging.
Example fix
// before
{"keepAliveTimeNanos": 1000.5}
// after
{"keepAliveTimeNanos": 1000} Defensive patterns
Strategy: validation
Validate before calling
Object v = config.get("keepAliveTimeNanos");
if (v instanceof Double && ((Double) v).longValue() != (Double) v) {
throw new IllegalArgumentException("keepAliveTimeNanos must be a whole long");
} Type guard
boolean isWholeLong(Object v) {
return v instanceof Double && ((Double) v).longValue() == (Double) v;
} Try / catch
try {
long l = JsonUtil.getNumberAsLong(config, "keepAliveTimeNanos");
} catch (ClassCastException e) {
log.error("Long config field has fractional value: " + e.getMessage());
} Prevention
- Compute nanosecond values with integer math, not floating point.
- Keep values within Long range/precision.
- Prefer writing whole numbers without decimal points in JSON.
When it happens
Trigger: JsonUtil.getNumberAsLong(map, key) where map.get(key) is a Double like 2.75, or a huge/precise double that no longer equals its longValue() (e.g. 1e19).
Common situations: Config values such as "keepAliveTimeNanos": 1000.5 from time-unit conversions done in floating point; oversized nanosecond values that exceed long precision when represented as doubles.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Number expected to be integer:
- 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' is not a double
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/991abfe38daa3ba8.
Report an issue: GitHub.