tursodatabase/turso · error · SQLException

Error reading Unicode stream

Error message

Error reading Unicode stream

What it means

setUnicodeStream(int, InputStream, int) reads up to 'length' bytes and decodes them as UTF-8; an IOException raised by stream.read() during that copy is wrapped as SQLException('Error reading Unicode stream') at JDBC4PreparedStatement.java:224 with the IOException as cause. The failure is in your stream, not the database - the binding never reaches the engine.

Solutions

  1. Unwrap the cause via SQLException.getCause() to see the underlying IOException and fix that resource.
  2. Read the whole stream into a byte[]/String before binding (readAllBytes + setString) so IO failures happen with clear stack traces under your control.
  3. Recreate streams from their source on every bind/retry - never reuse a consumed stream.
  4. Keep the stream's owner (file/socket/request) open for the duration of the setUnicodeStream call.

Example fix

// before
ps.setUnicodeStream(1, networkStream, len); // IOException inside -> SQLException

// after
byte[] bytes = networkStream.readAllBytes(); // explicit, retryable read
ps.setString(1, new String(bytes, StandardCharsets.UTF_8));
Defensive patterns

Strategy: try-catch

Validate before calling

// buffer before binding so IO failures happen under your control
byte[] bytes = stream.readAllBytes(); // throws IOException here, clearly
ps.setString(parameterIndex, new String(bytes, StandardCharsets.UTF_8));

Try / catch

try {
    ps.setUnicodeStream(i, in, length);
} catch (SQLException e) {
    if (e.getCause() instanceof IOException ioe) {
        // fix the stream source, recreate it, then retry the bind
    } else { throw e; }
}

Prevention

When it happens

Trigger: Stream already closed or previously consumed (single-use streams read once by logging or validation); underlying source vanished (closed socket, deleted temp file, timed-out HTTP stream); custom InputStream subclasses that throw from read(); passing a Reader wrapped naively as a stream with mismatched length.

Common situations: Retry frameworks reusing an exhausted stream between attempts; web-upload code binding request streams that the container already closed; streams over remote storage (S3, HDFS) whose connection dropped mid-copy; double-binding code that reads the same stream for two parameters.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/16e7ce6d9cdd9ebd. Report an issue: GitHub.

Appendix: source

Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java:224

    }
    if (length < 0) {
      throw new SQLException("setUnicodeStream length must be non-negative");
    }
    if (length == 0) {
      setParam(parameterIndex, "");
      return;
    }
    try {
      byte[] buffer = new byte[length];
      int offset = 0;
      int read;
      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()) {

View on GitHub (pinned to 244cde92a7)