hibernate/hibernate-orm · error · HibernateException

IOException occurred reading a binary value

Error message

IOException occurred reading a binary value

What it means

Thrown by BlobJavaType.unwrap when a raw (non-BlobImplementer) Blob is converted to byte[]: it calls value.getBinaryStream().readAllBytes() and an IOException during the read is wrapped in HibernateException('IOException occurred reading a binary value'). Unlike the SQLException variants, this means the stream opened but broke mid-read: connection dropped, stream closed underneath, or the driver could not deliver the full LOB payload.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java:137

			return null;
		}

		try {
			if ( Blob.class.isAssignableFrom( type ) ) {
				return type.cast( options.getLobCreator().toJdbcBlob( value ) );
			}
			else if ( byte[].class.isAssignableFrom( type )) {
				if (value instanceof BlobImplementer blobImplementer) {
					// if the incoming Blob is a wrapper, just grab the bytes from its BinaryStream
					return type.cast( blobImplementer.getUnderlyingStream().getBytes() );
				}
				else {
					try {
						// otherwise extract the bytes from the stream manually
						return type.cast( value.getBinaryStream().readAllBytes() );
					}
					catch ( IOException e ) {
						throw new HibernateException( "IOException occurred reading a binary value", e );
					}
				}
			}
			else if ( BinaryStream.class.isAssignableFrom( type ) ) {
				if (value instanceof BlobImplementer blobImplementer) {
					return type.cast( blobImplementer.getUnderlyingStream() );
				}
				else {
					return type.cast( new StreamBackedBinaryStream( value.getBinaryStream(), value.length() ) );
				}
			}
			else if ( InputStream.class.isAssignableFrom( type ) ) {
				if (value instanceof BlobImplementer blobImplementer) {
					// if the incoming Blob is a wrapper, just pass along its BinaryStream
					return type.cast( blobImplementer.getUnderlyingStream().getInputStream() );
				}
				else {
					// otherwise we need to build a BinaryStream...

View on GitHub (pinned to fad1729dce)

Solutions

  1. Keep the session/transaction open until the byte[] has been fully read.
  2. Raise driver/pool timeouts (socketTimeout, connection idle timeout) for connections that stream large LOBs.
  3. Map byte[] directly with @Lob so Hibernate materializes the value during the row read.
  4. If the connection was invalidated, retry the load once on a fresh session.

Example fix

// before
@Lob private java.sql.Blob data;
byte[] bytes = entity.getData().getBinaryStream().readAllBytes(); // after tx -> IOException

// after
@Lob private byte[] data; // materialized eagerly by Hibernate
byte[] bytes = entity.getData();
Defensive patterns

Strategy: retry

Validate before calling

// fail fast if the connection is already suspect
if (!connection.isValid(2)) throw new IllegalStateException("Connection unhealthy; reload instead of streaming LOB");

Try / catch

try {
    bytes = blob.getBinaryStream().readAllBytes();
} catch (IOException | HibernateException e) {
    // stream broke mid-read: one retry on a fresh session is usually enough
    return retryInNewSession(id);
}

Prevention

When it happens

Trigger: unwrap(..., byte[].class) on a plain JDBC Blob (loading a byte[] attribute from a BLOB column without Hibernate's wrapper); reading a large BLOB when the socket times out or the connection is invalidated mid-stream; reading the stream after it was already consumed or the statement was closed.

Common situations: Large binary columns (documents, images) exceeding socket/driver buffer or timeout settings; pool eviction (idle-in-transaction) while streaming a LOB; load-after-commit access patterns.

Related errors


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