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

  1. Inspect SQLException.getCause() for the real IOException and address that resource (reopen, reconnect, retry).
  2. Prefetch the payload: read it into byte[] (or a local temp file) first, then bind with setBytes or a fresh FileInputStream.
  3. Never reuse a consumed stream - recreate it from the source for each bind and each retry.
  4. 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

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


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)