hibernate/hibernate-orm · error · SQLException

Start position [<start>] cannot exceed overall CLOB length [

Error message

Start position [<start>] cannot exceed overall CLOB length [<length>]

What it means

In BlobProxy.getBinaryStream(long start, long length), start is checked against length() (the total blob size). A start greater than the total length means there is no byte at that position, so the proxy throws this SQLException. The message text says 'CLOB length' although this is a Blob proxy - a known copy/paste wart in the Hibernate source; the failing API is getBinaryStream(long, long) on a Blob.

Source

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

	}

	@Override
	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. Bound the read: if (start <= blob.length()) ... before calling getBinaryStream(start, len)
  2. End chunked loops when the previous read returned fewer bytes than requested instead of pushing start past the end
  3. Special-case empty blobs (length() == 0): skip reading entirely

Example fix

// before
InputStream in = blob.getBinaryStream(start, chunk); // start may exceed length

// after
if (start <= blob.length()) {
    InputStream in = blob.getBinaryStream(start, chunk);
} else {
    return InputStream.nullInputStream();
}
Defensive patterns

Strategy: validation

Validate before calling

static InputStream window(java.sql.Blob blob, long start, long length) throws SQLException {
    long total = blob.length();
    if (start < 1) throw new IllegalArgumentException("start must be >= 1");
    if (start > total) return InputStream.nullInputStream(); // nothing at/after the end
    return blob.getBinaryStream(start, length);
}

Try / catch

try {
    in = blob.getBinaryStream(start, length);
} catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().contains("cannot exceed overall")) {
        in = InputStream.nullInputStream();          // past the end: empty
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling blob.getBinaryStream(start, len) with start > blob.length() (e.g. 100 on a 50-byte blob); a chunked-read loop whose start cursor runs one chunk past the end; using a stale cached length after the blob was replaced by a smaller one.

Common situations: Pagination/chunking over LOBs with a loop condition like while (true) { ... start += chunk; } without checking start <= length; empty blobs (length 0) where any start >= 1 exceeds the length; race between checking length() and the entity being reloaded.

Related errors


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