hibernate/hibernate-orm · error · HibernateException

Unable to access lob stream

Error message

Unable to access lob stream

What it means

ByteArrayJavaType.wrap converts an incoming java.sql.Blob to Byte[] by calling DataHelper.extractBytes(blob.getBinaryStream()); a SQLException during extraction is wrapped as HibernateException('Unable to access lob stream'). Same locator-lifetime failure as the other Blob errors, but on the wrap (read side) path.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java:148

	public <X> Byte[] wrap(X value, WrapperOptions options) {
		if ( value == null ) {
			return null;
		}
		if (value instanceof Byte[] bytes) {
			return bytes;
		}
		if (value instanceof byte[] bytes) {
			return wrapBytes( bytes );
		}
		if (value instanceof InputStream inputStream) {
			return wrapBytes( DataHelper.extractBytes( inputStream ) );
		}
		if ( value instanceof Blob blob ) {
			try {
				return wrapBytes( DataHelper.extractBytes( blob.getBinaryStream() ) );
			}
			catch ( SQLException e ) {
				throw new HibernateException( "Unable to access lob stream", e );
			}
		}

		throw unknownWrap( value.getClass() );
	}

	private Byte[] wrapBytes(byte[] bytes) {
		if ( bytes == null ) {
			return null;
		}
		final var result = new Byte[bytes.length];
		for ( int i = 0; i < bytes.length; i++ ) {
			result[i] = bytes[i];
		}
		return result;
	}

	private byte[] unwrapBytes(Byte[] bytes) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Convert the Blob to bytes inside the owning transaction.
  2. Have the query return byte[] directly, or map the attribute as byte[] with @Lob.
  3. Copy the bytes immediately after retrieval via session.doReturningWork if you must cross a transaction boundary.

Example fix

// before
Byte[] data = session.doWork(...); // Blob fetched in one tx, wrapped in another

// after
byte[] bytes = session.doReturningWork((conn, rs) -> /* read bytes now, inside live tx */);
entity.setData(bytes);
Defensive patterns

Strategy: try-catch

Validate before calling

try {
    blob.length(); // probe the locator before wrap()
} catch (SQLException e) {
    throw new IllegalStateException("Cannot wrap a dead Blob locator", e);
}

Try / catch

try {
    return byteArrayJavaType.wrap(blob, options);
} catch (HibernateException e) {
    if (e.getCause() instanceof SQLException) {
        return wrapBytesFromFreshLoad(id); // re-read inside a live tx
    }
    throw e;
}

Prevention

When it happens

Trigger: wrap() receiving a raw JDBC Blob whose locator is dead: setting a Byte[] attribute from a stored-function result or native query returning Blob; converting after the owning transaction ended.

Common situations: Native queries and StoredProcedureQuery returning Blob into Byte[] fields; moving data across sessions; Oracle temporary LOB lifetime ending at commit.

Related errors


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