{"record":{"id":"990194b26d193a2d","repo":"hibernate/hibernate-orm","slug":"start-position-1-based-must-be-1-or-more","errorCode":null,"errorMessage":"Start position 1-based; must be 1 or more.","messagePattern":"Start position 1-based; must be 1 or more\\.","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java","lineNumber":148,"sourceCode":"\t *\n\t * @param stream The input stream of bytes to be created as a Blob.\n\t * @param length The number of bytes from stream to be written to the Blob.\n\t *\n\t * @return The BlobProxy instance to represent this data.\n\t */\n\tpublic static Blob generateProxy(InputStream stream, long length) {\n\t\treturn new BlobProxy( stream, length );\n\t}\n\n\t@Override\n\tpublic long length() throws SQLException {\n\t\treturn binaryStream.getLength();\n\t}\n\n\t@Override\n\tpublic byte[] getBytes(final long start, final int length) throws SQLException {\n\t\tif ( start < 1 ) {\n\t\t\tthrow new SQLException( \"Start position 1-based; must be 1 or more.\" );\n\t\t}\n\t\tif ( length < 0 ) {\n\t\t\tthrow new SQLException( \"Length must be great-than-or-equal to zero.\" );\n\t\t}\n\t\treturn DataHelper.extractBytes( getStream(), start-1, length );\n\t}\n\n\t@Override\n\tpublic InputStream getBinaryStream() throws SQLException {\n\t\treturn getStream();\n\t}\n\n\t@Override\n\tpublic long position(byte[] pattern, long start) {\n\t\tthrow notSupported();\n\t}\n\n\t@Override","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java#L130-L166","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (0-based thinking)\nbyte[] first = blob.getBytes(0, 16); // throws SQLException: start must be >= 1\n\n// after (JDBC is 1-based)\nbyte[] first = blob.getBytes(1, 16);","handlingStrategy":"validation","validationCode":"static byte[] readBytes(java.sql.Blob blob, long zeroBasedOffset, int length) throws SQLException {\n    long start = zeroBasedOffset + 1;              // JDBC positions are 1-based\n    if (start < 1) throw new IndexOutOfBoundsException(\"offset must be >= 0\");\n    if (length < 0) throw new IllegalArgumentException(\"length must be >= 0\");\n    return blob.getBytes(start, length);\n}","typeGuard":null,"tryCatchPattern":"try {\n    byte[] data = blob.getBytes(start, length);\n} catch (SQLException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Start position\")) {\n        // off-by-one: retry with the 1-based equivalent of your offset\n        data = blob.getBytes(offset + 1, length);\n    } else {\n        throw e;\n    }\n}","preventionTips":["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"],"tags":["hibernate","jdbc","blob","lob","off-by-one","indexing","sql-exception"],"backgroundTag":"jdbc-lob-position-validation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}