hibernate/hibernate-orm · error · HibernateException

Unable to access clob stream

Error message

Unable to access clob stream

What it means

ClobJavaType.unwrap wraps any SQLException raised while calling getCharacterStream()/length() during Clob to String/Reader/CharacterStream conversions into HibernateException('Unable to access clob stream'). It is the character-LOB twin of the blob-stream error: the locator is dead or the driver refused the operation.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClobJavaType.java:131

				}
				else {
					// otherwise we need to build a CharacterStream...
					return type.cast( value.getCharacterStream() );
				}
			}
			else if ( CharacterStream.class.isAssignableFrom( type ) ) {
				if (value instanceof ClobImplementer clobImplementer) {
					// if the incoming Clob is a wrapper, just pass along its CharacterStream
					return type.cast( clobImplementer.getUnderlyingStream() );
				}
				else {
					// otherwise we need to build a CharacterStream...
					return type.cast( new CharacterStreamImpl( value.getCharacterStream(), value.length() ) );
				}
			}
		}
		catch ( SQLException e ) {
			throw new HibernateException( "Unable to access clob stream", e );
		}

		throw unknownUnwrap( type );
	}

	public <X> Clob wrap(X value, WrapperOptions options) {
		if ( value == null ) {
			return null;
		}
		else {
			final LobCreator lobCreator = options.getLobCreator();
			if ( value instanceof Clob clob ) {
				return lobCreator.wrap( clob );
			}
			else if ( value instanceof String string ) {
				return lobCreator.createClob( string );
			}
			else if ( value instanceof Reader reader ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Convert the Clob to String inside the transaction that loaded it.
  2. Map the attribute as String with @Lob instead of java.sql.Clob.
  3. Re-fetch the entity in a fresh session before any conversion.

Example fix

// before
Clob clob = detachedItem.getText();
String text = clob.getSubString(1, (int) clob.length()); // after tx -> wrapped SQLException

// after
@Lob private String text; // materialized by Hibernate during read
String value = item.getText();
Defensive patterns

Strategy: try-catch

Validate before calling

try {
    clob.length(); // probe locator liveness before unwrap
} catch (SQLException e) {
    throw new IllegalStateException("Clob locator is dead; re-load the entity", e);
}

Try / catch

try {
    return clobJavaType.unwrap(clob, String.class, options);
} catch (HibernateException e) {
    if (e.getCause() instanceof SQLException) {
        return reloadAndGetText(id); // fresh session inside a live tx
    }
    throw e;
}

Prevention

When it happens

Trigger: Unwrapping a raw java.sql.Clob to String/Reader after its transaction or connection ended; converting a detached entity's Clob field; consuming a Clob returned by a stored function after the call completed.

Common situations: Post-commit access to Clob attributes; Oracle temporary CLOBs freed at commit; connection-pool recycling during long reads; OSIV disabled in web apps.

Related errors


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