tursodatabase/turso · error · SQLException
Error reading binary stream
Error message
Error reading binary stream
What it means
setBinaryStream(int, InputStream, int) copies 'length' bytes from your InputStream into the parameter; any IOException from stream.read() during the copy is wrapped as SQLException('Error reading binary stream') at JDBC4PreparedStatement.java:254 with the original IOException as the cause. The database is not involved yet - the read fails inside the driver before binding. The two-argument overload and setBytes have the same requirement that the stream be open and readable.
Solutions
- Inspect SQLException.getCause() for the real IOException and address that resource (reopen, reconnect, retry).
- Prefetch the payload: read it into byte[] (or a local temp file) first, then bind with setBytes or a fresh FileInputStream.
- Never reuse a consumed stream - recreate it from the source for each bind and each retry.
- Ensure the stream's owning resource stays open for the whole setBinaryStream call.
Example fix
// before ps.setBinaryStream(1, s3ObjectStream, len); // connection may drop -> SQLException // after byte[] data = downloadWithRetry(s3ObjectStream); // explicit, retryable ps.setBytes(1, data);
Defensive patterns
Strategy: try-catch
Validate before calling
// prefetch payload so stream health is verified before the bind byte[] data = stream.readAllBytes(); // explicit IO error handling here ps.setBytes(parameterIndex, data);
Try / catch
try {
ps.setBinaryStream(i, in, length);
} catch (SQLException e) {
if (e.getCause() instanceof IOException ioe) {
// reopen the source (file/socket), rebuild the stream, retry once
} else { throw e; }
} Prevention
- Prefetch remote/blob content to a local temp file or byte[] before binding.
- Recreate streams per bind attempt; streams are single-use.
- Keep the owning resource open for the whole call.
- Log the chained IOException cause - it names the real broken resource.
When it happens
Trigger: Stream already closed (closed FileInputStream, closed socket stream, container-closed request body); stream consumed earlier by another reader (logging wrapper, checksum computation, a previous failed bind attempt); network-backed streams whose connection dropped mid-copy; custom InputStream implementations throwing from read().
Common situations: Upload pipelines binding HTTP request streams that were drained or closed by middleware; retry loops reusing a one-shot stream; temp files deleted between staging and bind; large BLOB inserts over flaky remote storage (S3/HDFS) without prefetching.
Related errors
- Error reading ASCII stream
- Error reading Unicode stream
- setBinaryStream length must be non-negative
- setAsciiStream length must be non-negative
- setUnicodeStream length must be non-negative
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/80271aced849e782.
Report an issue: GitHub.
Appendix: 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 244cde92a7)