hibernate/hibernate-orm · error · HibernateException

Unable to access blob stream

Error message

Unable to access blob stream

What it means

Thrown by BlobJavaType.toString(Blob) when Hibernate materializes a java.sql.Blob into its string form. The method opens value.getBinaryStream() and fully reads it via DataHelper.extractBytes; any SQLException from the driver (invalid or freed locator, closed connection, driver limit) is wrapped in HibernateException with this message. Blob locators are only valid while the connection/transaction that produced them is still alive.

Source

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

	@Override
	public Blob cast(Object value) {
		return (Blob) value;
	}

	@Override
	public String extractLoggableRepresentation(Blob value) {
		return value == null ? "null" : "{blob}";
	}

	@Override
	public String toString(Blob value) {
		final byte[] bytes;
		try {
			bytes = extractBytes( value.getBinaryStream() );
		}
		catch ( SQLException e ) {
			throw new HibernateException( "Unable to access blob stream", e );
		}
		return PrimitiveByteArrayJavaType.INSTANCE.toString( bytes );
	}

	@Override
	public Blob fromString(CharSequence string) {
		return BlobProxy.generateProxy( PrimitiveByteArrayJavaType.INSTANCE.fromString( string ) );
	}

	@Override
	public int extractHashCode(Blob value) {
		return System.identityHashCode( value );
	}

	@Override
	public boolean areEqual(Blob one, Blob another) {
		return one == another;
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Exclude Blob attributes from toString()/logging; never stringify live LOB handles.
  2. Map the attribute as byte[] with @Lob instead of java.sql.Blob so the data is materialized eagerly at read time.
  3. Access and convert the Blob inside the same open session/transaction that loaded it.
  4. If the stream was consumed, re-query the entity in a fresh session before converting.

Example fix

// before
@Lob @Basic(fetch = FetchType.LAZY)
private java.sql.Blob data;
log.info("loaded {}", entity); // toString() touches the Blob -> HibernateException

// after
@Lob
private byte[] data; // materialized during row read
log.info("loaded {} bytes", entity.getData().length);
Defensive patterns

Strategy: try-catch

Validate before calling

// only convert while the session that loaded the Blob is alive
if (!session.isOpen() || !session.isConnected()) {
    throw new IllegalStateException("Reload the entity in an open session before touching the Blob");
}

Try / catch

try {
    return PrimitiveByteArrayJavaType.INSTANCE.toString(extractBytes(blob.getBinaryStream()));
} catch (HibernateException e) {
    if (e.getCause() instanceof SQLException) {
        // locator died: re-load in a fresh transaction or surface a domain error
        throw new IllegalStateException("Blob for entity " + id + " is no longer readable", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling toString()/logging on a detached entity that holds a Blob attribute; dirty-check or TRACE logging that stringifies the Blob; converting a Blob-typed query result to String after the transaction committed; Oracle freeing a temporary LOB on commit, or the stream already having been consumed once.

Common situations: Entities with java.sql.Blob @Lob fields included in toString()/log statements; access after the session closed (OSIV disabled); batch jobs carrying entities across transactions; driver-specific LOB lifetime quirks (Oracle temp LOBs, PostgreSQL Large Objects).

Related errors


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