{"record":{"id":"5f5427a13d410409","repo":"hibernate/hibernate-orm","slug":"unable-to-coerce-double-value-s-as-integer-not","errorCode":null,"errorMessage":"Unable to coerce Double value `%s` as Integer: not a whole number","messagePattern":"Unable to coerce Double value `(.+?)` as Integer: not a whole number","errorType":"exception","errorClass":"CoercionException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java","lineNumber":282,"sourceCode":"\tpublic static Integer toInteger(BigDecimal value) {\n\t\treturn coerceWrappingError( value::intValueExact );\n\t}\n\n\tpublic static Long toLong(Byte value) {\n\t\treturn value.longValue();\n\t}\n\n\tpublic static Long toLong(Short value) {\n\t\treturn value.longValue();\n\t}\n\n\tpublic static Long toLong(Integer value) {\n\t\treturn value.longValue();\n\t}\n\n\tpublic static Long toLong(Double doubleValue) {\n\t\tif ( ! isWholeNumber( doubleValue ) ) {\n\t\t\tthrow new CoercionException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Unable to coerce Double value `%s` as Integer: not a whole number\",\n\t\t\t\t\t\t\tdoubleValue\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\treturn doubleValue.longValue();\n\t}\n\n\tpublic static Long toLong(Float floatValue) {\n\t\tif ( ! isWholeNumber( floatValue ) ) {\n\t\t\tthrow new CoercionException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Unable to coerce Float value `%s` as Integer: not a whole number\",\n\t\t\t\t\t\t\tfloatValue\n\t\t\t\t\t)","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java#L264-L300","documentation":"Hibernate throws this CoercionException when a Double with a fractional part is coerced to Long. Note the message text is misleading: CoercionHelper.toLong(Double) says \"as Integer\" although the target type is Long — a copy-paste slip in Hibernate itself, so grep for toLong(LongJavaType) semantics, not Integer, when debugging. The check itself (isWholeNumber) is correct and fires from LongJavaType.coerce when a fractional Double reaches a Long-mapped attribute.","triggerScenarios":"`LongJavaType.coerce(1.5)` — assigning a Double to a Long/long entity field (IDs, counts, timestamps) via a Number/Object-typed setter, or `setParameter(\"id\", 123.45)` against a Long-typed path; e.g. a JSON id field deserialized as Double then set on a Long id-like attribute.","commonSituations":"JavaScript-produced JSON where all numbers parse as Double on the Java side and get copied onto Long fields (ids, epoch millis); Jackson/Gson loosely typed maps feeding Long attributes; analytics pipelines returning doubles for counts; developers confused by the \"Integer\" wording in the message when the real mismatch is Long.","solutions":["Fix the payload/producer: send integral JSON numbers and type DTO fields as Long, or convert explicitly with a whole-number check plus `longValue()`.","Round when truncation is agreed: `Math.round(d)` before assigning to the Long field.","Store fractional data in DOUBLE/DECIMAL columns instead of BIGINT.","When reading this error, remember the message says Integer but the failing conversion is Double -> Long (known Hibernate message bug)."],"exampleFix":"// before\nObject rawId = jsonMap.get(\"orderId\");   // Double 922337203685477580.5 style values or 123.45\norder.setExternalRef(rawId);             // Long field -> CoercionException (misleadingly says \"as Integer\")\n\n// after\nDouble d = (Double) rawId;\nif (d != Math.rint(d)) throw new IllegalArgumentException(\"id must be whole: \" + d);\norder.setExternalRef(d.longValue());\n// better: type the DTO field as Long so Jackson deserializes integrally","handlingStrategy":"validation","validationCode":"Object raw = json.get(\"orderId\");\nif (raw instanceof Double d) {\n    if (d != Math.rint(d)) throw new IllegalArgumentException(\"id must be whole: \" + d);\n    entity.setExternalRef(d.longValue());\n} else {\n    entity.setExternalRef(((Number) raw).longValue());\n}","typeGuard":"static boolean isWholeDoubleForLong(Double d) { return d != null && d == Math.rint(d) && Math.abs(d) < 9.007199254740992E15; } // also guards 2^53 precision limit","tryCatchPattern":"catch CoercionException; note the message wrongly says \"as Integer\" — match on stack trace (LongJavaType/CoercionHelper.toLong) when routing the error.","preventionTips":["Serialize ids as strings or integral numbers; never route ids through double.","Type DTO fields Long, not Object, for BIGINT-backed attributes.","Remember the message-text bug: Double->Long failures report as \"Integer\".","Add contract tests asserting ids round-trip through JSON without float conversion."],"tags":["hibernate","type-coercion","long","double","fractional","misleading-message"],"backgroundTag":"fractional-to-integer-coercion","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}