{"record":{"id":"ed9dbea1ddf01fb0","repo":"hibernate/hibernate-orm","slug":"start-position-start-cannot-exceed-overall-clo","errorCode":null,"errorMessage":"Start position [<start>] cannot exceed overall CLOB length [<length>]","messagePattern":"Start position \\[<start>\\] cannot exceed overall CLOB length \\[<length>\\]","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java","lineNumber":202,"sourceCode":"\t}\n\n\t@Override\n\tpublic void truncate(long len) {\n\t\tthrow notSupported();\n\t}\n\n\t@Override\n\tpublic void free() {\n\t\tbinaryStream.release();\n\t}\n\n\t@Override\n\tpublic InputStream getBinaryStream(final long start, final long 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 ( start > length() ) {\n\t\t\tthrow new SQLException( \"Start position [\" + start + \"] cannot exceed overall CLOB length [\" + length() + \"]\" );\n\t\t}\n\t\tif ( length > Integer.MAX_VALUE ) {\n\t\t\tthrow new SQLException( \"Can't deal with Blobs larger than 'Integer.MAX_VALUE'\" );\n\t\t}\n\t\tif ( length < 0 ) {\n\t\t\t// javadoc for getBinaryStream(long,int) specifies that the start+length must not exceed the\n\t\t\t// total length (this is at odds with the behavior of getBytes(long,int))\n\t\t\tthrow new SQLException( \"Length must be great-than-or-equal to zero.\" );\n\t\t}\n\t\treturn DataHelper.subStream( getStream(), start-1, (int)length );\n\t}\n\n\tprivate static UnsupportedOperationException notSupported() {\n\t\treturn new UnsupportedOperationException( \"Blob may not be manipulated from creating session\" );\n\t}\n\n}\n","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java#L184-L220","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Bound the read: if (start <= blob.length()) ... before calling getBinaryStream(start, len)","End chunked loops when the previous read returned fewer bytes than requested instead of pushing start past the end","Special-case empty blobs (length() == 0): skip reading entirely"],"exampleFix":"// before\nInputStream in = blob.getBinaryStream(start, chunk); // start may exceed length\n\n// after\nif (start <= blob.length()) {\n    InputStream in = blob.getBinaryStream(start, chunk);\n} else {\n    return InputStream.nullInputStream();\n}","handlingStrategy":"validation","validationCode":"static InputStream window(java.sql.Blob blob, long start, long length) throws SQLException {\n    long total = blob.length();\n    if (start < 1) throw new IllegalArgumentException(\"start must be >= 1\");\n    if (start > total) return InputStream.nullInputStream(); // nothing at/after the end\n    return blob.getBinaryStream(start, length);\n}","typeGuard":null,"tryCatchPattern":"try {\n    in = blob.getBinaryStream(start, length);\n} catch (SQLException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"cannot exceed overall\")) {\n        in = InputStream.nullInputStream();          // past the end: empty\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always compare start against blob.length() before positioned reads","End chunking loops when a window returns short, not when an exception fires","Remember the message says 'CLOB' even for Blobs - do not let that mislead diagnosis"],"tags":["hibernate","jdbc","blob","lob","out-of-range","off-by-one","sql-exception"],"backgroundTag":"jdbc-lob-position-exceeds-length","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}