tursodatabase/turso · error · SQLException
setBinaryStream length must be non-negative
Error message
setBinaryStream length must be non-negative
What it means
JDBC4PreparedStatement.setBinaryStream(int, InputStream, int) validates that length is >= 0 and throws SQLException at JDBC4PreparedStatement.java:236 otherwise. The driver copies exactly 'length' bytes from the stream into a BLOB parameter; a -1 'unknown length' sentinel, accepted by some other drivers, is rejected here. Use the two-argument setBinaryStream(int, InputStream) overload when the size is unknown.
Solutions
- Switch to setBinaryStream(parameterIndex, stream) - it reads to EOF and needs no length.
- Or buffer the payload (Files.readAllBytes / readAllBytes) and use setBytes(index, bytes).
- Compute the length from a reliable source (file length, array length) and fail fast yourself if it cannot be determined.
- Audit generic wrapper code for forwarded lengths and validate length >= 0 at the boundary.
Example fix
// before ps.setBinaryStream(1, fileIn, -1); // -1 sentinel -> SQLException // after ps.setBinaryStream(1, fileIn); // reads to EOF // or: ps.setBytes(1, Files.readAllBytes(path));
Defensive patterns
Strategy: validation
Validate before calling
if (length < 0) {
ps.setBinaryStream(parameterIndex, stream); // unknown length: read to EOF
} else {
ps.setBinaryStream(parameterIndex, stream, length);
} Try / catch
try {
ps.setBinaryStream(i, in, length);
} catch (SQLException e) {
if (e.getMessage().contains("non-negative")) { ps.setBinaryStream(i, in); } else { throw e; }
} Prevention
- Use the two-argument overload or setBytes when size is unknown.
- Treat any -1 from a size probe as 'compute it properly', never forward it.
- Validate lengths at wrapper boundaries before reaching the driver.
- Prefer byte[] buffering for file-backed payloads.
When it happens
Trigger: Passing -1 (or any negative int) as length, commonly the 'read to end' convention from other drivers; lengths derived from File.length() cast incorrectly, subtraction underflow, or -1 returned by a size-lookup helper; wrapper APIs forwarding user-supplied lengths unchecked; null stream is allowed (binds NULL), only negative length throws.
Common situations: File-upload code where the size probe failed and -1 leaked into the bind call; generic JDBC layers ported from MySQL Connector/J or the PostgreSQL driver; big-binary insert code written against tutorials using the 3-arg API; tests with placeholder lengths.
Related errors
- Error reading binary stream
- setAsciiStream length must be non-negative
- setUnicodeStream length must be non-negative
- Error reading ASCII stream
- Error reading Unicode stream
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/ec4c7e9750944ff4.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java:236
while (offset < length && (read = x.read(buffer, offset, length - offset)) > 0) {
offset += read;
}
String text = new String(buffer, 0, offset, StandardCharsets.UTF_8);
setParam(parameterIndex, text);
} catch (IOException e) {
throw new SQLException("Error reading Unicode stream", e);
}
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
requireNonNull(this.statement);
if (x == null) {
setParam(parameterIndex, null);
return;
}
if (length < 0) {
throw new SQLException("setBinaryStream length must be non-negative");
}
if (length == 0) {
setParam(parameterIndex, new byte[0]);
return;
}
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
int totalRead = 0;
while (totalRead < length
&& (bytesRead = x.read(buffer, 0, Math.min(buffer.length, length - totalRead))) > 0) {
baos.write(buffer, 0, bytesRead);
totalRead += bytesRead;
}
byte[] data = baos.toByteArray();
setParam(parameterIndex, data);
} catch (IOException e) {
throw new SQLException("Error reading binary stream", e);View on GitHub (pinned to 244cde92a7)