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
- Convert the Blob to bytes inside the owning transaction.
- Have the query return byte[] directly, or map the attribute as byte[] with @Lob.
- 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
- Copy Blob bytes out inside the transaction that produced the Blob.
- Avoid passing raw JDBC Blobs between layers; materialize early.
- Map byte[] with @Lob when streaming is unnecessary.
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
- Unable to set BLOB bytes after creation
- Could not create JDBC Blob
- Start position 1-based; must be 1 or more.
- Length must be great-than-or-equal to zero.
- Start position [<start>] cannot exceed overall CLOB length [
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/8e5fe8d52f487d52.
Report an issue: GitHub.