tursodatabase/turso · error · SQLException

setAsciiStream length must be non-negative

Error message

setAsciiStream length must be non-negative

What it means

Thrown by setAsciiStream(int, InputStream, int) when the caller passes a negative length. The JDBC contract requires length to be a non-negative byte count, and this driver validates it up front, before allocating the read buffer, so a bad length is rejected at bind time without reading the stream. Note the neighboring cases: a null stream binds SQL NULL and a zero length binds an empty string; only negative lengths throw.

Source

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

  public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
    requireNonNull(this.statement);
    if (x == null) {
      setParam(parameterIndex, null);
    } else {
      long time = x.getTime();
      setParam(parameterIndex, ByteBuffer.allocate(Long.BYTES).putLong(time).array());
    }
  }

  @Override
  public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
    requireNonNull(this.statement);
    if (x == null) {
      setParam(parameterIndex, null);
      return;
    }
    if (length < 0) {
      throw new SQLException("setAsciiStream 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 ascii = new String(buffer, 0, offset, StandardCharsets.US_ASCII);
      setParam(parameterIndex, ascii);
    } catch (IOException e) {
      throw new SQLException("Error reading ASCII stream", e);
    }
  }

View on GitHub (pinned to bad083fafb)

Solutions

  1. Pass an exact non-negative byte count to setAsciiStream(int, InputStream, int).
  2. If the length is unknown, call the no-length overload setAsciiStream(int, InputStream), which reads the stream until EOF.
  3. To bind SQL NULL, pass a null stream (already handled by the driver) or use setNull(i, Types.VARCHAR) instead of a length trick.

Example fix

// before
ps.setAsciiStream(1, in, -1); // throws: length must be non-negative

// after
ps.setAsciiStream(1, in); // reads the stream until EOF
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().contains("non-negative")) {
        // programmer error in the length argument — fix the caller, do not retry
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling ps.setAsciiStream(i, in, -1); forwarding a computed length that underflowed or defaulted to -1; porting code that uses the InputStream.read() convention where -1 means 'read to EOF'.

Common situations: Reusable DAO layers that accept an optional length and forward -1 when the size is unknown; lengths from unsigned sources stored into a signed int; code copied from drivers where -1 meant 'unspecified'.

Related errors


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