hibernate/hibernate-orm · error · CoercionException

Unable to coerce Float value `%s` as Integer: not a whole nu

Error message

Unable to coerce Float value `%s` as Integer: not a whole number

What it means

Hibernate throws this CoercionException when a Float with a fractional part is coerced to Long. As with its Double sibling, the message text is wrong: CoercionHelper.toLong(Float) says "as Integer" even though the target is Long — a copy-paste artifact in Hibernate, so trust the stack trace (LongJavaType.coerce -> toLong) over the message wording. The isWholeNumber guard fires from LongJavaType.coerce whenever a fractional Float reaches a Long-mapped attribute.

Source

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

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

	public static Long toLong(BigInteger value) {
		return coerceWrappingError( value::longValueExact );
	}

	public static Long toLong(BigDecimal value) {
		return coerceWrappingError( value::longValueExact );
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Fix the producer to emit integral types, or convert explicitly: verify whole-number then `floatValue()`/`Math.round(f)` before assigning to the Long field.
  2. Store fractional values in FLOAT/DOUBLE columns rather than BIGINT.
  3. Validate incoming Numbers for whole-number-ness at the boundary.
  4. Read the stack trace, not the message: this variant is the Float -> Long case despite the Integer wording.

Example fix

// before
Float elapsed = timer.elapsedFraction();  // e.g. 12.9f
job.setDuration(elapsed);                 // Long 'duration' -> CoercionException (message says "as Integer")

// after
job.setDuration((long) Math.round(elapsed));
// or map duration as Float if sub-second fractions matter
Defensive patterns

Strategy: validation

Validate before calling

Float f = timer.fraction();
if (f != Math.rint(f)) throw new IllegalArgumentException("duration must be whole: " + f);
job.setDuration((long) Math.round(f));

Type guard

static boolean isWholeFloatForLong(Float f) { return f != null && f == Math.rint(f) && !f.isNaN() && !f.isInfinite(); }

Try / catch

catch CoercionException; the message says "as Integer" but the target is Long — use the stack trace to classify; map to a validation error.

Prevention

When it happens

Trigger: `LongJavaType.coerce(2.5f)` — assigning a Float to a Long/long field (counts, durations, ids) through a Number/Object-typed setter or dynamic map, or `setParameter("n", 3.7f)` bound against a Long-typed path.

Common situations: Float-valued durations or scores from UIs/simulations persisted into BIGINT-mapped Long fields; loosely typed Map<String,Object> payloads; developers misled by the "Integer" text in the message while actually debugging a Long mapping; analytics feeds emitting floats for integral counts.

Related errors


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