apache/dubbo · error · ClassCastException
Number expected to be long: ${d}
Error message
Number expected to be long: ${d} What it means
Thrown by AbstractJsonUtilImpl.getNumberAsLong(Map,String) when obj contains key, the value is a Double, but the long cast loses precision — i.e. d.longValue() != d. This guards against silently truncating a fractional Double (e.g. 3.5) into a long. It is a ClassCastException naming the offending Double. Only the Double branch reaches it; String values go through Long.parseLong.
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/AbstractJsonUtilImpl.java:185
/**
* 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.
*/
@Override
public Long getNumberAsLong(Map<String, ?> obj, String key) {
assert obj != null;
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 3a3043227f)
Solutions
- If truncation/rounding is acceptable, pre-round: Math.round(d) before reading.
- If the fractional value is a data error, fix the producer to emit whole numbers.
- Switch to getNumberAsDouble if the field is genuinely non-integral.
- Catch ClassCastException and treat as a validation error.
Example fix
// before
Long id = jsonUtil.getNumberAsLong(obj, "id"); // id = 42.7
// after - round then read
Object raw = obj.get("id");
Long id = raw instanceof Double ? Math.round((Double) raw) : jsonUtil.getNumberAsLong(obj, "id"); Defensive patterns
Strategy: type-guard
Validate before calling
Object raw = obj.get(key);
if (raw instanceof Double) {
double d = (Double) raw;
if (d != Math.rint(d)) {
return Math.round(d); // round before reading as long
}
}
return jsonUtil.getNumberAsLong(obj, key); Type guard
private static boolean isWholeDouble(Object value) {
if (!(value instanceof Double)) return value instanceof String;
double d = (Double) value;
return d == Math.rint(d);
} Try / catch
try {
return jsonUtil.getNumberAsLong(obj, key);
} catch (ClassCastException e) {
Object raw = obj.get(key);
if (raw instanceof Double) return Math.round((Double) raw); // recover
throw e;
} Prevention
- Round fractional Doubles before reading them as long.
- Watch for floating accumulation error on IDs/timestamps.
- If the field is genuinely fractional, use getNumberAsDouble instead.
When it happens
Trigger: jsonUtil.getNumberAsLong(obj, key) where obj.get(key) is a Double with a non-zero fractional part (e.g. 1.5, 99.9). Whole-valued doubles pass; anything off-integer throws.
Common situations: A field that is usually a count (integral) but occasionally a ratio/average; floating accumulation error turning 5 into 5.0000001; a Double-typed upstream widened from a fractional source; an ID/timestamp mistakenly encoded with a decimal portion.
Related errors
- Number expected to be integer: ${d}
- 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
- value '%s' for key '%s' in '%s' is not a number
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/c9204348a6f4617a.
Report an issue: GitHub.