hibernate/hibernate-orm · error · HibernateException

Unable to access lob stream

Error message

Unable to access lob stream

What it means

PrimitiveByteArrayJavaType.wrapOrNull() converts an incoming java.sql.Blob into byte[] by calling getBinaryStream() and fully reading it; any SQLException from the driver (invalidated locator, stream already consumed, connection closed, LOB freed) is wrapped as HibernateException("Unable to access lob stream"). Like the NClob variant, this almost always means the Blob handle outlived the transaction or connection that owned it.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java:159

		if ( wrapped == null ) {
			throw unknownWrap( value.getClass() );
		}
		return wrapped;
	}

	private <X> @Nullable byte[] wrapOrNull(@Nonnull X value) {
		if (value instanceof byte[] bytes) {
			return bytes;
		}
		if (value instanceof InputStream inputStream) {
			return DataHelper.extractBytes( inputStream );
		}
		if ( value instanceof Blob blob ) {
			try {
				return DataHelper.extractBytes( blob.getBinaryStream() );
			}
			catch ( SQLException e ) {
				throw new HibernateException( "Unable to access lob stream", e );
			}
		}
		else if ( value instanceof Byte byteValue ) {
			// Support binding a single element as parameter value
			return new byte[]{ byteValue };
		}
		else if ( value instanceof Byte[] array ) {
			final byte[] bytes = new byte[array.length];
			for ( int i = 0; i < array.length; i++ ) {
				bytes[i] = array[i];
			}
			return bytes;
		}
		return null;
	}

	@Override
	public @Nullable byte[] coerce(@Nullable Object value) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Materialize byte[] content while the session and transaction are open (eager fetch or explicit read in service code)
  2. Keep the session open until the content is read, or refresh the entity inside a new transaction before accessing the field
  3. Avoid holding live java.sql.Blob handles across requests - copy to byte[] immediately after load
  4. Check driver settings/docs regarding temporary LOB lifetime at commit (Oracle temp LOBs)

Example fix

// before
Attachment a = repo.findById(id).get(); // session closes after tx
byte[] data = a.getData(); // lazy Blob wrap after close -> HibernateException

// after
@Transactional(readOnly = true)
byte[] loadNow(Long id) {
    return repo.findById(id).map(Attachment::getData).orElseThrow(); // read inside tx
}
// or map the column directly as byte[] with eager materialization
Defensive patterns

Strategy: try-catch

Validate before calling

static byte[] materializeInTx(jakarta.persistence.EntityManager em, Long id) {
    return em.executeInTransaction(() -> {
        Attachment a = em.find(Attachment.class, id);
        return a == null ? null : a.getData(); // Blob -> byte[] while locator is live
    });
}

Type guard

static boolean isUsable(java.sql.Blob b) {
    try { b.length(); return true; } catch (java.sql.SQLException e) { return false; }
}

Try / catch

try {
    return attachment.getData();
} catch (HibernateException e) {
    if ("Unable to access lob stream".equals(e.getMessage()) && e.getCause() instanceof java.sql.SQLException) {
        return reloadInsideTransaction(attachment.getId()); // fresh locator, not a blind retry
    }
    throw e;
}

Prevention

When it happens

Trigger: A byte[] attribute mapped from a BLOB column is lazily materialized after the session/transaction closed; the Blob stream is read twice or partially consumed elsewhere; drivers (Oracle, DB2) that auto-release temporary LOBs at commit

Common situations: Lazy-loaded attachments read in a view layer after the transaction ended; entities serialized/deserialized across requests with live Blob handles; background jobs processing entities after their session ended

Related errors


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