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; any IOException from the stream during the copy is wrapped in this SQLException. As with the ASCII variant, the statement never executes — the failure is in reading the caller's stream at bind time.

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 bad083fafb)

Solutions

  1. Inspect SQLException.getCause() for the real IOException and fix the source (keep it alive, add retries).
  2. Open and bind the stream within one try-with-resources scope.
  3. Migrate off the deprecated setUnicodeStream to setCharacterStream with an InputStreamReader(UTF-8) while you are fixing the lifecycle.

Example fix

// before
ps.setUnicodeStream(1, in, len); // in was closed elsewhere

// after
try (Reader r = new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8)) {
    ps.setCharacterStream(1, r, len);
}
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.setUnicodeStream(1, in, len);
} catch (SQLException e) {
    if (e.getCause() instanceof IOException ioe) {
        // stream failure — reopen source or fail with context; also plan migration to setCharacterStream
    }
    throw e;
}

Prevention

When it happens

Trigger: Already-closed InputStream; file deleted or rotated after open; network-backed stream resetting during the bounded read; GZIP stream failing mid-decompression.

Common situations: File cleanup jobs racing the bind; streaming from remote object storage; a stream consumed earlier by logging or checksumming code.

Related errors


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