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();
	}

	@Override

View on GitHub (pinned to fad1729dce)

Solutions

  1. Pass a 1-based start: use getBytes(offset + 1, length) when offset is 0-based
  2. In chunked-read loops, iterate start from 1 and stop at blob.length()
  3. If offsets keep causing bugs, call getBinaryStream() once and slice the resulting byte array instead
  4. 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

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


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