hibernate/hibernate-orm · error · CoercionException

Cannot coerce Double value `%s` to Byte : not a whole number

Error message

Cannot coerce Double value `%s` to Byte : not a whole number

What it means

Hibernate throws this CoercionException when a Double must be coerced to Byte but has a fractional part. CoercionHelper.toByte(Double) first checks isWholeNumber(value); any value like 3.14 fails before the range checks run. It is reached through ByteJavaType.coerce when a Double value is assigned to or bound against a Byte-mapped attribute. Hibernate treats fractional-to-integral coercion as lossy and rejects it rather than truncating.

Source

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

			);
		}

		if ( value < Byte.MIN_VALUE ) {
			throw new CoercionException(
					String.format(
							Locale.ROOT,
							"Cannot coerce Long value `%s` to Byte : underflow",
							value
					)
			);
		}

		return value.byteValue();
	}

	public static Byte toByte(Double value) {
		if ( ! isWholeNumber( value ) ) {
			throw new CoercionException(
					String.format(
							Locale.ROOT,
							"Cannot coerce Double value `%s` to Byte : not a whole number",
							value
					)
			);
		}

		if ( value > Byte.MAX_VALUE ) {
			throw new CoercionException(
					String.format(
							Locale.ROOT,
							"Cannot coerce Double value `%s` to Byte : overflow",
							value
					)
			);
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Fix the producer: round/quantize before assignment (`(byte) Math.round(d)`) only if truncation semantics are acceptable, otherwise store the value in a Double/BigDecimal-typed column.
  2. Change the attribute type to Double/BigDecimal if fractional values are legitimate.
  3. Sanitize incoming data at the boundary: reject or round non-whole numbers before they reach the entity.
  4. Re-check any HQL/Criteria expressions feeding Byte paths; cast or round in the query (`floor()`, `round()`) as appropriate.

Example fix

// before
double avg = readings.stream().mapToDouble(r -> r.value).average().orElse(0);
sensor.setCalibration(avg);      // Byte field -> "not a whole number"

// after
sensor.setCalibration((byte) Math.round(avg));
// or make calibration a Double column if fractions matter
Defensive patterns

Strategy: validation

Validate before calling

Double d = computed;
if (d != Math.rint(d) || Double.isNaN(d) || Double.isInfinite(d)) {
    throw new IllegalArgumentException("value must be a whole number: " + d);
}
entity.setByteField((byte) (long) (double) d); // after additional range check

Type guard

static boolean isWhole(Double d) { return d != null && !d.isNaN() && !d.isInfinite() && d == Math.rint(d); }

Try / catch

try { session.persist(e); } catch (CoercionException ex) { /* convert to domain validation error, surface to caller */ }

Prevention

When it happens

Trigger: `ByteJavaType.coerce(3.14)` — e.g. `entity.setRatio(computedDouble)` on a Byte/byte attribute, or `setParameter("b", 2.5)` against a Byte-typed path, or HQL/Criteria arithmetic (averages, division) producing Double results compared/assigned to a Byte attribute.

Common situations: Computed metrics (averages, percentages) stored into tinyint-mapped Byte fields; JSON numbers with decimals (Jackson deserializes as Double) copied onto Byte properties; unit bugs (storing 0.5 kg in a field meant for whole grams); switching an attribute from Double to Byte during a model refactor while old writers still send fractions.

Related errors


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