tursodatabase/turso · error · SQLException
setUnicodeStream length must be non-negative
Error message
setUnicodeStream length must be non-negative
What it means
JDBC4PreparedStatement.setUnicodeStream(int, InputStream, int) rejects a negative length with SQLException at JDBC4PreparedStatement.java:208 before touching the stream. This deprecated JDBC 1 method decodes the stream as UTF-8; the driver requires an explicit non-negative byte length and does not accept -1 as an 'unknown length' sentinel. Null streams are accepted and bind NULL.
Solutions
- Migrate off the deprecated API: use setCharacterStream or setNString for unicode text, or setString directly.
- If you must use it, compute and pass the exact non-negative byte length of the UTF-8 payload.
- Buffer the stream into a String first, then call setString - removes length bookkeeping entirely.
- Validate length >= 0 at the boundary of your wrapper and fail with your own clear message.
Example fix
// before ps.setUnicodeStream(1, utf8In, -1); // negative sentinel -> SQLException // after String text = new String(utf8In.readAllBytes(), StandardCharsets.UTF_8); ps.setString(1, text);
Defensive patterns
Strategy: validation
Validate before calling
if (length < 0) {
String text = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
ps.setString(parameterIndex, text);
} else {
ps.setUnicodeStream(parameterIndex, stream, length);
} Try / catch
try {
ps.setUnicodeStream(i, in, length);
} catch (SQLException e) {
if (e.getMessage().contains("non-negative")) { /* recompute length or setString */ } else { throw e; }
} Prevention
- Migrate off the deprecated setUnicodeStream to setString/setCharacterStream.
- Pass exact non-negative byte lengths when the old API is required.
- Reject -1 sentinels at your wrapper's boundary with a clear message.
- Compute UTF-8 byte length, not char count, for this parameter.
When it happens
Trigger: Passing -1 as a length sentinel (a convention tolerated by some other drivers); length computed from a char count, remaining bytes, or an underflowing calculation; forwarding caller-supplied lengths without validation in a generic data layer; test code using placeholder lengths.
Common situations: Legacy code still on the deprecated setUnicodeStream API being pointed at turso; wrappers/frameworks that mirror the old JDBC 1 trio (setAsciiStream/setUnicodeStream/setBinaryStream); migrations where the original database accepted loose length semantics.
Related errors
- Error reading Unicode stream
- setAsciiStream length must be non-negative
- setBinaryStream length must be non-negative
- Error reading ASCII stream
- Error reading binary stream
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/f21c3394310bc09a.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java:208
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);
}
}
@Override
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
requireNonNull(this.statement);
if (x == null) {
setParam(parameterIndex, null);
return;
}
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);
}
}View on GitHub (pinned to 244cde92a7)