hibernate/hibernate-orm · error · SQLException

Can't deal with Blobs larger than 'Integer.MAX_VALUE'

Error message

Can't deal with Blobs larger than 'Integer.MAX_VALUE'

What it means

BlobProxy.getBinaryStream(long start, long length) can only produce a sub-stream whose length fits an int, because it ultimately casts length to int for DataHelper.subStream. A length argument above Integer.MAX_VALUE is rejected with this SQLException rather than being silently truncated.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java:205

	public void truncate(long len) {
		throw notSupported();
	}

	@Override
	public void free() {
		binaryStream.release();
	}

	@Override
	public InputStream getBinaryStream(final long start, final long length) throws SQLException {
		if ( start < 1 ) {
			throw new SQLException( "Start position 1-based; must be 1 or more." );
		}
		if ( start > length() ) {
			throw new SQLException( "Start position [" + start + "] cannot exceed overall CLOB length [" + length() + "]" );
		}
		if ( length > Integer.MAX_VALUE ) {
			throw new SQLException( "Can't deal with Blobs larger than 'Integer.MAX_VALUE'" );
		}
		if ( length < 0 ) {
			// javadoc for getBinaryStream(long,int) specifies that the start+length must not exceed the
			// total length (this is at odds with the behavior of getBytes(long,int))
			throw new SQLException( "Length must be great-than-or-equal to zero." );
		}
		return DataHelper.subStream( getStream(), start-1, (int)length );
	}

	private static UnsupportedOperationException notSupported() {
		return new UnsupportedOperationException( "Blob may not be manipulated from creating session" );
	}

}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Read in chunks: loop with length capped at e.g. 8 MB per call
  2. For whole-stream access use getBinaryStream() (no args) and stream-copy instead of fixed windows
  3. Replace 'read all' sentinels like Long.MAX_VALUE with the real remaining length

Example fix

// before
InputStream all = blob.getBinaryStream(1, Long.MAX_VALUE); // throws

// after
InputStream all = blob.getBinaryStream(); // full stream, copy incrementally
Defensive patterns

Strategy: validation

Validate before calling

static final long MAX_WINDOW = 8L * 1024 * 1024; // 8 MiB per call
static long cappedWindow(long requested, long remaining) {
    if (requested < 0) throw new IllegalArgumentException("length must be >= 0");
    return Math.min(Math.min(requested, remaining), MAX_WINDOW);
}
// usage: blob.getBinaryStream(start, cappedWindow(len, blob.length() - start + 1))

Try / catch

try {
    in = blob.getBinaryStream(start, length);
} catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().contains("Integer.MAX_VALUE")) {
        in = blob.getBinaryStream();                 // fall back to the full stream
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling blob.getBinaryStream(1, blob.length()) when the blob exceeds Integer.MAX_VALUE bytes; passing Long.MAX_VALUE as an 'until the end' marker; generic copy utilities that forward a user-supplied long maxSize.

Common situations: Multi-GB LOBs (video, genomics, backups) stored in databases with huge-lob support; helper APIs that accept long sizes from HTTP Range headers; migrating from drivers that clamp silently to Hibernate's proxy which fails fast.

Related errors


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