tursodatabase/turso · error · SQLException

setBinaryStream length must be non-negative

Error message

setBinaryStream length must be non-negative

What it means

Thrown by setBinaryStream(int, InputStream, int) when length is negative. The driver validates the length before entering its 8 KiB chunked copy loop: a null stream binds SQL NULL and a zero length binds an empty byte array; only negative lengths are rejected at bind time.

Source

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

      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()) {
      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);

View on GitHub (pinned to bad083fafb)

Solutions

  1. Pass a non-negative byte count to setBinaryStream(int, InputStream, int).
  2. Use the no-length overload setBinaryStream(int, InputStream) when the size is unknown — it drains the stream to EOF.
  3. To bind NULL, pass a null stream or call setNull(i, Types.BLOB).

Example fix

// before
long size = sizeOf(file); // returns -1 on error
ps.setBinaryStream(1, in, (int) size);

// after
ps.setBinaryStream(1, in); // reads until EOF, no length needed
Defensive patterns

Strategy: validation

Validate before calling

if (length < 0) {
    throw new IllegalArgumentException("length must be >= 0, got " + length);
}
ps.setBinaryStream(1, in, length);

Try / catch

catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().contains("non-negative")) {
        // fix the length argument or switch to the no-length overload
    }
    throw e;
}

Prevention

When it happens

Trigger: ps.setBinaryStream(i, in, -1); a file-size lookup that returned -1 (common error-code convention) forwarded directly as length; unsigned sizes widened into a negative int.

Common situations: Size probes (File.length() on a missing file returns 0, but custom lookups often return -1); code ported from drivers accepting -1 as 'unspecified'; length arithmetic that underflows.

Related errors


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