tursodatabase/turso · error · SQLException
Error reading InputStream
Error message
Error reading InputStream
What it means
The no-length setAsciiStream(int, InputStream) and setBinaryStream(int, InputStream) overloads drain the whole stream eagerly via the readBytes helper: one probe byte, then 8 KiB chunks until EOF. Any IOException during that copy is wrapped in this SQLException. An empty stream is legal (probe byte == -1 binds an empty value); this error means the stream actually failed mid-read, at bind time — not later at execute.
Source
Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java:605
* @return a byte array containing the data
* @throws SQLException if an I/O error occurs while reading
*/
private byte[] readBytes(InputStream x) throws SQLException {
try {
int firstByte = x.read();
if (firstByte == -1) {
return new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(firstByte);
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = x.read(buffer)) > 0) {
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
} catch (IOException e) {
throw new SQLException("Error reading InputStream", e);
}
}
@Override
public void setCharacterStream(int parameterIndex, @Nullable Reader reader) throws SQLException {
requireNonNull(this.statement);
if (reader == null) {
setParam(parameterIndex, null);
return;
}
try {
StringBuilder sb = new StringBuilder();
char[] buffer = new char[8192];
int read;
while ((read = reader.read(buffer)) != -1) {
sb.append(buffer, 0, read);
}
setParam(parameterIndex, sb.toString());View on GitHub (pinned to bad083fafb)
Solutions
- Unwrap SQLException.getCause() for the original IOException and fix the producer (timeouts, retries, keep-alive).
- Stabilize remote streams first: read them fully into a byte[] with your own retry, then bind with setBytes; or download to a temp file.
- Open and bind the stream inside one try-with-resources block — the copy happens during the set* call itself.
Example fix
// before InputStream in = objectStore.open(key); // may reset mid-read ps.setBinaryStream(1, in); // after byte[] data = retryingReadAll(objectStore, key); // buffered + retried ps.setBytes(1, data);
Defensive patterns
Strategy: try-catch
Validate before calling
// fail fast if the stream is already closed
try {
in.available();
} catch (IOException e) {
throw new IllegalStateException("stream closed before binding", e);
} Try / catch
try {
ps.setBinaryStream(1, in);
} catch (SQLException e) {
if (e.getCause() instanceof IOException ioe) {
// stream failure: reopen/retry the source, or fail with context
}
throw e;
} Prevention
- The driver copies the stream during the set* call itself — closed-before-bind is the common failure mode.
- Buffer remote/untrusted streams (readAllBytes + retry) and bind with setBytes.
- Never share one stream between consumers; the driver drains it fully.
When it happens
Trigger: Already-closed or invalidated stream; HTTP/S3-backed stream resetting mid-copy; GZIP stream hitting corruption while being drained; file deleted after open.
Common situations: Object-storage streams without client-side retry; streams closed by an outer scope before the set* call; two components sharing one stream instance.
Related errors
- Error reading ASCII stream
- Error reading Unicode stream
- Error reading binary stream
- Error reading character stream
- setAsciiStream length must be non-negative
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/0edd5c395c430be8.
Report an issue: GitHub.