hibernate/hibernate-orm · error · CoercionException
Unable to coerce Double value `%s` as Integer: not a whole n
Error message
Unable to coerce Double value `%s` as Integer: not a whole number
What it means
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.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java:282
public static Integer toInteger(BigDecimal value) {
return coerceWrappingError( value::intValueExact );
}
public static Long toLong(Byte value) {
return value.longValue();
}
public static Long toLong(Short value) {
return value.longValue();
}
public static Long toLong(Integer value) {
return value.longValue();
}
public static Long toLong(Double doubleValue) {
if ( ! isWholeNumber( doubleValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double value `%s` as Integer: not a whole number",
doubleValue
)
);
}
return doubleValue.longValue();
}
public static Long toLong(Float floatValue) {
if ( ! isWholeNumber( floatValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Float value `%s` as Integer: not a whole number",
floatValue
)View on GitHub (pinned to fad1729dce)
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).
Example fix
// before
Object rawId = jsonMap.get("orderId"); // Double 922337203685477580.5 style values or 123.45
order.setExternalRef(rawId); // Long field -> CoercionException (misleadingly says "as Integer")
// after
Double d = (Double) rawId;
if (d != Math.rint(d)) throw new IllegalArgumentException("id must be whole: " + d);
order.setExternalRef(d.longValue());
// better: type the DTO field as Long so Jackson deserializes integrally Defensive patterns
Strategy: validation
Validate before calling
Object raw = json.get("orderId");
if (raw instanceof Double d) {
if (d != Math.rint(d)) throw new IllegalArgumentException("id must be whole: " + d);
entity.setExternalRef(d.longValue());
} else {
entity.setExternalRef(((Number) raw).longValue());
} Type guard
static boolean isWholeDoubleForLong(Double d) { return d != null && d == Math.rint(d) && Math.abs(d) < 9.007199254740992E15; } // also guards 2^53 precision limit Try / catch
catch CoercionException; note the message wrongly says "as Integer" — match on stack trace (LongJavaType/CoercionHelper.toLong) when routing the error.
Prevention
- 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.
When it happens
Trigger: `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.
Common situations: 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.
Related errors
- Unable to coerce Float value `%s` as Integer: not a whole nu
- Cannot coerce Double value `%s` to Byte : not a whole number
- Cannot coerce Double value `%s` as Short : not a whole numbe
- Unable to coerce Double value `%s` to Integer: not a whole n
- Unable to coerce Double value `%s` as BigInteger: not a whol
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/5f5427a13d410409.
Report an issue: GitHub.