{"record":{"id":"233a223c897a18d6","repo":"apache/seatunnel","slug":"stream-closed","errorCode":null,"errorMessage":"Stream closed","messagePattern":"Stream closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPInputStream.java","lineNumber":83,"sourceCode":"    @Override\n    public void seek(long position) throws IOException {\n        throw new IOException(E_SEEK_NOT_SUPPORTED);\n    }\n\n    @Override\n    public boolean seekToNewSource(long targetPos) throws IOException {\n        throw new IOException(E_SEEK_NOT_SUPPORTED);\n    }\n\n    @Override\n    public long getPos() throws IOException {\n        return pos;\n    }\n\n    @Override\n    public synchronized int read() throws IOException {\n        if (closed) {\n            throw new IOException(E_STREAM_CLOSED);\n        }\n\n        int byteRead = wrappedStream.read();\n        if (byteRead >= 0) {\n            pos++;\n        }\n        if (stats != null & byteRead >= 0) {\n            stats.incrementBytesRead(1);\n        }\n        return byteRead;\n    }\n\n    public synchronized int read(byte[] buf, int off, int len) throws IOException {\n        if (closed) {\n            throw new IOException(E_STREAM_CLOSED);\n        }\n\n        int result = wrappedStream.read(buf, off, len);","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPInputStream.java#L65-L101","documentation":"SFTPInputStream.read() throws IOException(\"Stream closed\") when the stream has already been closed via close(). The wrapper tracks a 'closed' flag and refuses any further reads on the wrapped SFTP input stream. This is a defensive guard against using a closed stream, which would otherwise fail deeper inside JSch with confusing errors.","triggerScenarios":"Calling read() after close() has been invoked on the SFTPInputStream; a reader loop that keeps reading after the connector closed the stream; double-closing and then reading.","commonSituations":"Downstream code closing the stream in a try-with-resources block but accidentally reading afterwards; retry logic reusing a stale stream object after task cancellation; reader thread racing with a close from another thread (close is not synchronized with read guard state in caller code).","solutions":["Ensure no read() calls happen after close(); restructure so the stream is read fully inside try-with-resources.","If the stream was closed due to cancellation/error, reopen a new SFTPInputStream instead of reusing the old object.","Check for double-close in wrapper code (e.g. closing both the wrapped stream and this stream then reading).","Guard concurrent access: reads and closes should be sequenced by the same thread or external synchronization."],"exampleFix":"// before\nInputStream in = openSftpStream();\nin.close();\nint b = in.read(); // IOException: Stream closed\n// after\ntry (InputStream in = openSftpStream()) {\n    int b = in.read(); // read while open\n}","handlingStrategy":"try-catch","validationCode":"if (in instanceof SFTPInputStream && ((SFTPInputStream) in).isClosed()) { reopenStream(); }","typeGuard":"boolean isReadable(InputStream in) { return in != null && !(in instanceof SFTPInputStream s) || !s.isClosed(); }","tryCatchPattern":"try { int b = in.read(); } catch (IOException e) { if (\"Stream closed\".equals(e.getMessage())) { in = reopenStream(); } else { throw e; } }","preventionTips":["Always read streams inside try-with-resources","Never reuse a stream object after close(); reopen instead","Keep close() and read() on the same thread or synchronize access","Initialize stream references inside the scope that consumes them"],"tags":["io","sftp","stream-closed","java"],"backgroundTag":"stream-closed","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}