hibernate/hibernate-orm · error · SQLException
Start position 1-based; must be 1 or more.
Error message
Start position 1-based; must be 1 or more.
What it means
Hibernate's BlobProxy implements java.sql.Blob for LOBs created outside an active JDBC transaction (e.g. Hibernate.getLobHelper().createBlob(byte[]) or createBlob(InputStream, long)). Its getBytes(long start, int length) validates start against the JDBC contract, which is 1-based: position 1 is the first byte. Passing 0 or a negative value throws this SQLException before any data is read.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java:148
*
* @param stream The input stream of bytes to be created as a Blob.
* @param length The number of bytes from stream to be written to the Blob.
*
* @return The BlobProxy instance to represent this data.
*/
public static Blob generateProxy(InputStream stream, long length) {
return new BlobProxy( stream, length );
}
@Override
public long length() throws SQLException {
return binaryStream.getLength();
}
@Override
public byte[] getBytes(final long start, final int length) throws SQLException {
if ( start < 1 ) {
throw new SQLException( "Start position 1-based; must be 1 or more." );
}
if ( length < 0 ) {
throw new SQLException( "Length must be great-than-or-equal to zero." );
}
return DataHelper.extractBytes( getStream(), start-1, length );
}
@Override
public InputStream getBinaryStream() throws SQLException {
return getStream();
}
@Override
public long position(byte[] pattern, long start) {
throw notSupported();
}
@OverrideView on GitHub (pinned to fad1729dce)
Solutions
- Pass a 1-based start: use getBytes(offset + 1, length) when offset is 0-based
- In chunked-read loops, iterate start from 1 and stop at blob.length()
- If offsets keep causing bugs, call getBinaryStream() once and slice the resulting byte array instead
- Add a unit test that reads the first byte with getBytes(1, 1) to pin the 1-based contract
Example fix
// before (0-based thinking) byte[] first = blob.getBytes(0, 16); // throws SQLException: start must be >= 1 // after (JDBC is 1-based) byte[] first = blob.getBytes(1, 16);
Defensive patterns
Strategy: validation
Validate before calling
static byte[] readBytes(java.sql.Blob blob, long zeroBasedOffset, int length) throws SQLException {
long start = zeroBasedOffset + 1; // JDBC positions are 1-based
if (start < 1) throw new IndexOutOfBoundsException("offset must be >= 0");
if (length < 0) throw new IllegalArgumentException("length must be >= 0");
return blob.getBytes(start, length);
} Try / catch
try {
byte[] data = blob.getBytes(start, length);
} catch (SQLException e) {
if (e.getMessage() != null && e.getMessage().contains("Start position")) {
// off-by-one: retry with the 1-based equivalent of your offset
data = blob.getBytes(offset + 1, length);
} else {
throw e;
}
} Prevention
- Encapsulate every Blob read in one helper that converts 0-based offsets to 1-based positions
- Write one test per helper asserting the first byte is at position 1
- Treat JDBC LOB positions like SQL SUBSTR/FIRE, never like array indices
When it happens
Trigger: Calling getBytes(0, n) or getBytes(-2, n) on a Hibernate-created Blob; feeding a 0-based loop counter or byte[] offset straight into getBytes; porting String.substring-style indexing (where 0 is valid) to the JDBC Blob API.
Common situations: Developers used to 0-based Java arrays using the first element's index as the Blob position; chunked-read loops written as for (int i = 0; i < len; i += chunk) blob.getBytes(i, chunk); test fixtures that assume array semantics; code copied from InputStream.read examples.
Related errors
- Start position [<start>] cannot exceed overall CLOB length [
- Start position 1-based; must be 1 or more.
- Length must be great-than-or-equal to zero.
- Can't deal with Blobs larger than 'Integer.MAX_VALUE'
- Start position [${start}] cannot exceed overall CLOB length
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/990194b26d193a2d.
Report an issue: GitHub.