hibernate/hibernate-orm · error · CoercionException

Cannot coerce Integer value `%s` to Byte : underflow

Error message

Cannot coerce Integer value `%s` to Byte : underflow

What it means

Hibernate throws this CoercionException (a HibernateException subclass) when a Java Integer value must be narrowed to java.lang.Byte (range -128..127) and the value is below -128. It is thrown from CoercionHelper.toByte(Integer), reached via ByteJavaType.coerce(...) during persist/merge/saveOrUpdate or query-parameter binding when the value handed to a Byte-mapped attribute is an Integer that cannot be represented as a byte. Unlike silent Java narrowing casts, Hibernate 6 refuses lossy conversions and fails fast with the offending value in the message (the `%s` is the input value).

Source

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

			);
		}

		return value.byteValue();
	}

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

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

		return value.byteValue();
	}

	public static Byte toByte(Long value) {
		if ( value > Byte.MAX_VALUE ) {
			throw new CoercionException(
					String.format(
							Locale.ROOT,
							"Cannot coerce Long value `%s` to Byte : overflow",
							value

View on GitHub (pinned to fad1729dce)

Solutions

  1. Fix the type mismatch at the source: either declare the entity attribute as Integer/Short to match the real value domain, or convert the value to byte before assignment (`(byte) intValue` or `intValue.byteValue()` after a range check).
  2. If narrowing is intentional and out-of-range data is impossible-by-contract, add an explicit javax.persistence.AttributeConverter<Integer,Byte> that documents and performs the narrowing.
  3. If out-of-range values are legitimate, change the column DDL (e.g. TINYINT -> SMALLINT/INT) and remap the attribute type accordingly.
  4. Add a range guard (-128..127) in the setter or at the import boundary and reject/clamp bad values before they reach the Session.

Example fix

// before
@Entity class Meter {
    Byte offset;              // tinyint column
}
meter.setOffset(payloadValue); // payloadValue is Integer -200 -> CoercionException

// after
@Entity class Meter {
    Integer offset;            // matches actual value domain
}
// or guard before assignment:
if (payloadValue >= Byte.MIN_VALUE && payloadValue <= Byte.MAX_VALUE) {
    meter.setOffset(payloadValue.byteValue());
} else {
    throw new IllegalArgumentException("offset out of byte range: " + payloadValue);
}
Defensive patterns

Strategy: validation

Validate before calling

Integer v = payload.getByteField();
if (v < Byte.MIN_VALUE || v > Byte.MAX_VALUE) {
    throw new IllegalArgumentException("value out of byte range: " + v);
}
entity.setByteField(v.byteValue());

Type guard

static boolean fitsInByte(Integer v) { return v != null && v >= Byte.MIN_VALUE && v <= Byte.MAX_VALUE; }

Try / catch

try { session.merge(entity); } catch (CoercionException e) { /* log offending value, reject the write */ } // CoercionException extends HibernateException

Prevention

When it happens

Trigger: Entity attribute declared `Byte`/`byte` (e.g. a tinyint column) but the code assigns an Integer below -128: `entity.setByteValue(-200)` where the setter takes Number/Object, or `session.createQuery(...).setParameter("b", -200)` bound against a Byte-typed path. Also fires when a Map<String,Object> payload or dynamic-model value containing an Integer is funneled into a Byte attribute via ByteJavaType.coerce.

Common situations: JSON payloads deserialized by Jackson into Integer/Object fields then copied onto a Byte entity property; dynamic maps or CSV import feeds; switching an attribute from Integer to Byte without cleaning stored data; moving from Hibernate 5 (which narrowed silently) to Hibernate 6 (which validates); unsigned TINYINT columns on MySQL/MariaDB holding values 128..255 that map to negative/oversized integers.

Related errors


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