{"record":{"id":"80271aced849e782","repo":"tursodatabase/turso","slug":"error-reading-binary-stream","errorCode":null,"errorMessage":"Error reading binary stream","messagePattern":"Error reading binary stream","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java","lineNumber":254,"sourceCode":"      throw new SQLException(\"setBinaryStream length must be non-negative\");\n    }\n    if (length == 0) {\n      setParam(parameterIndex, new byte[0]);\n      return;\n    }\n    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {\n      byte[] buffer = new byte[8192];\n      int bytesRead;\n      int totalRead = 0;\n      while (totalRead < length\n          && (bytesRead = x.read(buffer, 0, Math.min(buffer.length, length - totalRead))) > 0) {\n        baos.write(buffer, 0, bytesRead);\n        totalRead += bytesRead;\n      }\n      byte[] data = baos.toByteArray();\n      setParam(parameterIndex, data);\n    } catch (IOException e) {\n      throw new SQLException(\"Error reading binary stream\", e);\n    }\n  }\n\n  @Override\n  public void clearParameters() {\n    this.currentBatchParams = new Object[paramCount];\n  }\n\n  @Override\n  public void clearBatch() throws SQLException {\n    this.batchQueryParams.clear();\n    this.currentBatchParams = new Object[paramCount];\n  }\n\n  @Override\n  public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {\n    // TODO\n  }","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java#L236-L272","documentation":"setBinaryStream(int, InputStream, int) copies 'length' bytes from your InputStream into the parameter; any IOException from stream.read() during the copy is wrapped as SQLException('Error reading binary stream') at JDBC4PreparedStatement.java:254 with the original IOException as the cause. The database is not involved yet - the read fails inside the driver before binding. The two-argument overload and setBytes have the same requirement that the stream be open and readable.","triggerScenarios":"Stream already closed (closed FileInputStream, closed socket stream, container-closed request body); stream consumed earlier by another reader (logging wrapper, checksum computation, a previous failed bind attempt); network-backed streams whose connection dropped mid-copy; custom InputStream implementations throwing from read().","commonSituations":"Upload pipelines binding HTTP request streams that were drained or closed by middleware; retry loops reusing a one-shot stream; temp files deleted between staging and bind; large BLOB inserts over flaky remote storage (S3/HDFS) without prefetching.","solutions":["Inspect SQLException.getCause() for the real IOException and address that resource (reopen, reconnect, retry).","Prefetch the payload: read it into byte[] (or a local temp file) first, then bind with setBytes or a fresh FileInputStream.","Never reuse a consumed stream - recreate it from the source for each bind and each retry.","Ensure the stream's owning resource stays open for the whole setBinaryStream call."],"exampleFix":"// before\nps.setBinaryStream(1, s3ObjectStream, len); // connection may drop -> SQLException\n\n// after\nbyte[] data = downloadWithRetry(s3ObjectStream); // explicit, retryable\nps.setBytes(1, data);","handlingStrategy":"try-catch","validationCode":"// prefetch payload so stream health is verified before the bind\nbyte[] data = stream.readAllBytes(); // explicit IO error handling here\nps.setBytes(parameterIndex, data);","typeGuard":null,"tryCatchPattern":"try {\n    ps.setBinaryStream(i, in, length);\n} catch (SQLException e) {\n    if (e.getCause() instanceof IOException ioe) {\n        // reopen the source (file/socket), rebuild the stream, retry once\n    } else { throw e; }\n}","preventionTips":["Prefetch remote/blob content to a local temp file or byte[] before binding.","Recreate streams per bind attempt; streams are single-use.","Keep the owning resource open for the whole call.","Log the chained IOException cause - it names the real broken resource."],"tags":["java","jdbc","turso","stream","io","blob","parameter-binding"],"backgroundTag":"stream-read-ioexception","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}