{"record":{"id":"330c02d85828cea8","repo":"apache/dubbo","slug":"number-expected-to-be-integer-d","errorCode":null,"errorMessage":"Number expected to be integer: ${d}","messagePattern":"Number expected to be integer: (.+?)","errorType":"exception","errorClass":"ClassCastException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/AbstractJsonUtilImpl.java","lineNumber":153,"sourceCode":"    }\n\n    /**\n     * Gets a number from an object for the given key, casted to an integer.  If the key is not\n     * present, this returns null.  If the value does not represent an integer, throws an exception.\n     */\n    @Override\n    public Integer getNumberAsInteger(Map<String, ?> obj, String key) {\n        assert obj != null;\n        assert key != null;\n        if (!obj.containsKey(key)) {\n            return null;\n        }\n        Object value = obj.get(key);\n        if (value instanceof Double) {\n            Double d = (Double) value;\n            int i = d.intValue();\n            if (i != d) {\n                throw new ClassCastException(\"Number expected to be integer: \" + d);\n            }\n            return i;\n        }\n        if (value instanceof String) {\n            try {\n                return Integer.parseInt((String) value);\n            } catch (NumberFormatException e) {\n                throw new IllegalArgumentException(\n                        String.format(\"value '%s' for key '%s' is not an integer\", value, key));\n            }\n        }\n        throw new IllegalArgumentException(String.format(\"value '%s' for key '%s' is not an integer\", value, key));\n    }\n\n    /**\n     * Gets a number from an object for the given key, casted to an long.  If the key is not\n     * present, this returns null.  If the value does not represent a long integer, throws an\n     * exception.","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/AbstractJsonUtilImpl.java#L135-L171","documentation":"Thrown by AbstractJsonUtilImpl.getNumberAsInteger(Map,String) when obj contains key, the value is a Double, but its integral cast loses precision — i.e. d.intValue() != d. This guards against silently truncating a fractional value like 3.14 into 3. It is a ClassCastException (a type-shape mismatch) naming the offending Double. Only the Double branch can reach it; String values go through Integer.parseInt.","triggerScenarios":"jsonUtil.getNumberAsInteger(obj, key) where obj.get(key) is a Double with a non-zero fractional part, e.g. 3.5, 1.1, -2.9. Whole-valued doubles (3.0) pass; anything off-integer throws.","commonSituations":"A JSON field decoded as Double (3.0-ish backend) that occasionally carries a fractional part; a ratio/percentage mistakenly read as a count; a Float-typed upstream value widened to a fractional Double; a computed value that should be integral but accumulated floating error (e.g. 4.0000001).","solutions":["If a fractional value is a data error, fix the upstream producer to emit whole numbers.","If truncation/rounding is acceptable, pre-round the Double: Math.round(d) or (int) Math.rint(d), then call getNumberAsInteger or read it directly.","Switch to getNumberAsDouble if the field is genuinely non-integral.","Catch ClassCastException and treat fractional values as a validation error."],"exampleFix":"// before\nInteger n = jsonUtil.getNumberAsInteger(obj, \"qty\"); // qty = 3.7\n// after - round explicitly\nObject raw = obj.get(\"qty\");\nInteger n = (raw instanceof Double) ? (int) Math.round((Double) raw) : jsonUtil.getNumberAsInteger(obj, \"qty\");","handlingStrategy":"type-guard","validationCode":"Object raw = obj.get(key);\nif (raw instanceof Double) {\n    double d = (Double) raw;\n    if (d != Math.rint(d)) {\n        // fractional; decide rounding policy before calling getNumberAsInteger\n        return (int) Math.round(d);\n    }\n}\nreturn jsonUtil.getNumberAsInteger(obj, key);","typeGuard":"private static boolean isWholeDouble(Object value) {\n    if (!(value instanceof Double)) return value instanceof String;\n    double d = (Double) value;\n    return d == Math.rint(d);\n}","tryCatchPattern":"try {\n    return jsonUtil.getNumberAsInteger(obj, key);\n} catch (ClassCastException e) {\n    Object raw = obj.get(key);\n    if (raw instanceof Double) return (int) Math.round((Double) raw); // recover\n    throw e;\n}","preventionTips":["Round fractional Doubles (Math.round) before reading them as integers.","Watch for floating accumulation error that makes whole values look fractional.","If the field is genuinely fractional, use getNumberAsDouble instead."],"tags":["json","number-parsing","classcast","precision","jsonutil","dubbo-common"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}