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

  1. 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.
  2. 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.
  3. Validate whole-number-ness of incoming Numbers before assigning to BigInteger attributes.
  4. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/08992260aef8a25d. Report an issue: GitHub.