hibernate/hibernate-orm · error · CoercionException

Unable to coerce Double Float `%s` as BigInteger: not a whol

Error message

Unable to coerce Double Float `%s` as BigInteger: not a whole number

What it means

Thrown by CoercionHelper.toBigInteger(Float) when Hibernate coerces a Float into a BigInteger basic type and the isWholeNumber(floatValue) check fails, i.e. the value has a fractional part. Hibernate runs this coercion when a BigInteger attribute or query parameter receives a float-typed value. The odd wording 'Double Float' is a copy-paste artifact from the Double overload; the real meaning is simply that the Float is not a whole number.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java:329

		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
					)
			);
		}
		return BigInteger.valueOf( floatValue.longValue() );
	}

	public static BigInteger toBigInteger(BigDecimal value) {
		return coerceWrappingError( value::toBigIntegerExact );
	}

	public static Double toDouble(Float floatValue) {
		if ( floatValue > (float) Double.MAX_VALUE ) {
			throw new CoercionException(
					String.format(

View on GitHub (pinned to fad1729dce)

Solutions

  1. Round or truncate explicitly before assigning: BigInteger.valueOf(floatValue.longValue()) or Math.round, so the lossy conversion is intentional
  2. Change the entity attribute type to match the real data (BigDecimal or Double) instead of BigInteger
  3. Pass the exact target type from the start and remove float-typed intermediaries
  4. As a boundary guard, catch org.hibernate.type.descriptor.java.CoercionException and reject the input with a validation error

Example fix

// before
entity.setAmount(2.5f); // field is BigInteger -> CoercionException: not a whole number

// after
entity.setAmount(BigInteger.valueOf((long) 2.5f)); // explicit truncation
// or better: change the field to BigDecimal and keep the fraction
Defensive patterns

Strategy: validation

Validate before calling

static BigInteger toBigIntegerSafe(Float v) {
    if (v == null) return null;
    if (!v.isFinite() || v % 1.0f != 0.0f) {
        throw new IllegalArgumentException("not a whole number: " + v);
    }
    return BigInteger.valueOf(v.longValue());
}

Type guard

static boolean isWholeFloat(Number n) {
    return n instanceof Float f && f.isFinite() && f % 1.0f == 0.0f;
}

Try / catch

try {
    session.persist(entity);
} catch (CoercionException e) {
    // rethrow as a user-facing validation error naming the field
    throw new BadRequestException("value must be a whole number", e);
}

Prevention

When it happens

Trigger: Binding a fractional float such as setParameter("n", 2.5f) where the path/attribute is BigInteger; assigning a Float that came from JSON, a Map<String,Object>, or a reflective setter to a BigInteger entity field; result coercion when the declared Java type is BigInteger but the incoming value arrives boxed as Float.

Common situations: Loosely typed input (JSON payloads, row maps) mapped onto strict numeric entity fields; refactoring a field from Double/Float to BigInteger without cleaning producers; older service APIs that hand out Float for quantities now stored as integers.

Related errors


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