tursodatabase/turso · error · SQLException
Error reading binary stream
Error message
Error reading binary stream
What it means
setBinaryStream(int, InputStream, int) copies the stream through an 8 KiB buffer into a ByteArrayOutputStream; an IOException from x.read — the only call that can throw here, since ByteArrayOutputStream.write is unchecked — is wrapped in this SQLException. Note that a short stream does NOT throw: if EOF arrives before length bytes, the driver silently binds only the bytes actually read, so verify sizes separately if truncation matters.
Source
Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java:254
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);
}
}
@Override
public void clearParameters() {
this.currentBatchParams = new Object[paramCount];
}
@Override
public void clearBatch() throws SQLException {
this.batchQueryParams.clear();
this.currentBatchParams = new Object[paramCount];
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
// TODO
}View on GitHub (pinned to bad083fafb)
Solutions
- Inspect getCause() for the original IOException and fix the source stream lifecycle.
- Keep the source alive until the bind returns; open it in the same scope (try-with-resources).
- Buffer remote streams first (readAllBytes with your own retry) and bind with setBytes; also verify the byte count matches expectations to catch silent truncation.
Example fix
// before
ps.setBinaryStream(1, in, len); // in already closed elsewhere
// after
try (InputStream in = Files.newInputStream(path)) {
ps.setBinaryStream(1, in, (int) Files.size(path));
} Defensive patterns
Strategy: try-catch
Validate before calling
// fail fast if the stream is already closed
try {
in.available();
} catch (IOException closed) {
throw new IllegalStateException("stream closed before binding", closed);
} Try / catch
try {
ps.setBinaryStream(1, in, len);
} catch (SQLException e) {
if (e.getCause() instanceof IOException ioe) {
// stream failure — reopen/retry the source, or buffer first and use setBytes
}
throw e;
} Prevention
- Keep the source alive until the bind returns; open in try-with-resources.
- Remember short reads bind silently — verify byte counts if truncation matters.
- Buffer remote streams with your own retry before binding.
When it happens
Trigger: Already-closed stream; file deleted mid-read; network stream reset while the copy loop is running; truncated upload being bound with its advertised length.
Common situations: Streams from HTTP/S3 dropping mid-transfer; files removed by retention jobs between open and bind; silent truncation going unnoticed because short reads do not error.
Related errors
- Error reading ASCII stream
- Error reading Unicode stream
- Error reading character stream
- Error reading InputStream
- setAsciiStream length must be non-negative
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/80271aced849e782.
Report an issue: GitHub.