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
- Unwrap the cause via SQLException.getCause() to see the underlying IOException and fix that resource.
- Read the whole stream into a byte[]/String before binding (readAllBytes + setString) so IO failures happen with clear stack traces under your control.
- Recreate streams from their source on every bind/retry - never reuse a consumed stream.
- 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
- Never reuse a consumed stream; recreate from source.
- Ensure the stream's owner (file/socket/request) stays open during the bind.
- Prefetch remote content to local storage before streaming into JDBC.
- Check e.getCause() first - the real failure is the chained IOException.
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
- Error reading ASCII stream
- Error reading binary stream
- setUnicodeStream length must be non-negative
- setAsciiStream length must be non-negative
- setBinaryStream length must be non-negative
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)