hibernate/hibernate-orm · error · CoercionException
Unable to coerce Double value `%s` as BigInteger: not a whol
Error message
Unable to coerce Double value `%s` as BigInteger: not a whole number
What it means
Hibernate throws this CoercionException when a Double with a fractional part is coerced to BigInteger. CoercionHelper.toBigInteger(Double) checks isWholeNumber(doubleValue) and rejects values like 1e10 + 0.5 before building the BigInteger via BigInteger.valueOf(longValue()). It is reached from BigIntegerJavaType.coerce when a Double value is supplied to a BigInteger-mapped attribute. Hibernate blocks the lossy fractional-to-integral conversion instead of truncating toward zero.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java:316
"Unable to coerce Float value `%s` as Integer: not a whole number",
floatValue
)
);
}
return floatValue.longValue();
}
public static Long toLong(BigInteger value) {
return coerceWrappingError( value::longValueExact );
}
public static Long toLong(BigDecimal value) {
return coerceWrappingError( value::longValueExact );
}
public static BigInteger toBigInteger(Double doubleValue) {
if ( ! isWholeNumber( doubleValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double value `%s` as BigInteger: not a whole number",
doubleValue
)
);
}
return BigInteger.valueOf( doubleValue.longValue() );
}
public static BigInteger toBigInteger(Float floatValue) {
if ( ! isWholeNumber( floatValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double Float `%s` as BigInteger: not a whole number",
floatValue
)View on GitHub (pinned to fad1729dce)
Solutions
- Fix serialization end to end: emit big ids as JSON strings or integral numbers and type DTO fields BigInteger/Long so they never pass through double.
- Round explicitly if truncation is acceptable: `BigInteger.valueOf(Math.round(d))` — but for ids > 2^53 double already lost precision, so repair the source data instead.
- Validate whole-number-ness of incoming Numbers before assigning to BigInteger attributes.
- Store genuinely fractional magnitudes in BigDecimal with a DECIMAL column, not BigInteger.
Example fix
// before
Object rawId = jsonResponse.get("transactionId"); // Double 3.52e18 with fraction/precision loss
txn.setTxnId(rawId); // BigInteger field -> "not a whole number"
// after
// server sends ids as strings: {"transactionId":"3520000000000000001"}
txn.setTxnId(new BigInteger((String) jsonResponse.get("transactionId")));
// never route big ids through double/Double Defensive patterns
Strategy: validation
Validate before calling
Object raw = response.get("transactionId");
if (raw instanceof Double d) {
if (d != Math.rint(d) || Math.abs(d) >= 9.007199254740992E15) {
throw new IllegalArgumentException("id lost whole-number/precision status in double: " + d);
}
txn.setTxnId(BigInteger.valueOf(d.longValue()));
} else if (raw instanceof String s) {
txn.setTxnId(new BigInteger(s));
} else {
txn.setTxnId((BigInteger) raw);
} Type guard
static boolean isExactBigIntegerCandidate(Object o) { return o instanceof BigInteger || o instanceof Long l || (o instanceof String s && s.matches("-?\\d+")); } Try / catch
catch CoercionException and reject the payload — for big ids the double representation has already lost precision, so recovery requires re-reading the original source, not rounding.
Prevention
- Never serialize ids larger than 2^53 as JSON numbers; use strings.
- Type DTO id fields BigInteger/Long, never Object/Number/Double.
- Validate id fields lexically (digits only) at the boundary.
- Prefer BigDecimal mappings for monetary magnitudes with fractions.
When it happens
Trigger: `BigIntegerJavaType.coerce(1234.56)` — assigning a Double to a BigInteger entity field via a Number/Object-typed setter, or binding a fractional double as a query parameter against a BigInteger-typed path; typical with ids (Snowflake-style), large counters, or monetary magnitudes computed as double.
Common situations: Snowflake/ULID ids serialized as Double in JSON (losing precision AND failing whole-number check) then mapped onto BigInteger fields; financial computations in double feeding NUMERIC-mapped BigInteger attributes; JavaScript frontends sending large ids as floats; Jackson loosely typed deserialization into Object (Double) copied onto BigInteger properties.
Related errors
- 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 Integer: not a whole n
- Unable to coerce value [%s (%s)] to BigInteger
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/08992260aef8a25d.
Report an issue: GitHub.