{"record":{"id":"ec5cff4835a5e068","repo":"hibernate/hibernate-orm","slug":"length-must-be-great-than-or-equal-to-zero","errorCode":null,"errorMessage":"Length must be great-than-or-equal to zero.","messagePattern":"Length must be great-than-or-equal to zero\\.","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java","lineNumber":151,"sourceCode":"\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\n\tpublic long position(Blob pattern, long start) {\n\t\tthrow notSupported();\n\t}","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java#L133-L169","documentation":"BlobProxy.getBytes(long start, int length) rejects a negative length with this SQLException. The proxy honors the JDBC contract where a read of zero bytes is legal but a negative count is a caller bug, and it fails fast before touching the underlying stream.","triggerScenarios":"Calling blob.getBytes(1, -1) on a Hibernate-created Blob; computing length as end - start and passing it negative when end < start; a 'read remainder' helper defaulting length to -1 on error paths.","commonSituations":"Length arithmetic bugs such as length = endIndex - startIndex with reversed or clamped indices; sentinel values (-1) leaking from indexOf/position() results into getBytes; unit tests probing invalid input.","solutions":["Pass length >= 0; use 0 when you only want to validate start","Clamp computed lengths: int len = Math.max(0, end - start)","Never pipe indexOf/position() return values (-1 when not found) into getBytes without checking"],"exampleFix":"// before\nint len = end - start; // may be negative\nbyte[] data = blob.getBytes(1, len);\n\n// after\nint len = Math.max(0, end - start);\nbyte[] data = blob.getBytes(1, len);","handlingStrategy":"validation","validationCode":"static byte[] safeGetBytes(java.sql.Blob blob, long start, int length) throws SQLException {\n    if (start < 1) throw new IllegalArgumentException(\"start must be 1-based (>= 1)\");\n    if (length < 0) length = 0;                     // negative read is a bug: read nothing\n    return blob.getBytes(start, length);\n}","typeGuard":null,"tryCatchPattern":"try {\n    data = blob.getBytes(start, length);\n} catch (SQLException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Length must be\")) {\n        data = blob.getBytes(start, 0);             // degrade to empty read\n    } else {\n        throw e;\n    }\n}","preventionTips":["Clamp computed lengths with Math.max(0, end - start) before any LOB call","Never forward -1 sentinels from indexOf/position() as lengths","Centralize length arithmetic in one tested helper"],"tags":["hibernate","jdbc","blob","lob","negative-length","argument-validation","sql-exception"],"backgroundTag":"jdbc-lob-negative-length","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}