{"record":{"id":"511a6be1e269a4b0","repo":"juicedata/juicefs","slug":"stream-was-closed","errorCode":null,"errorMessage":"stream was closed","messagePattern":"stream was closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java","lineNumber":1075,"sourceCode":"    private final Path path;\n\n    private ByteBuffer buf;\n    private long position;\n    private long fileLen;\n\n    public FileInputStream(Path f, int fd, int size, long fileLen) throws IOException {\n      path = f;\n      this.fd = fd;\n      buf = directBufferPool.getBuffer(size);\n      buf.limit(0);\n      position = 0;\n      this.fileLen = fileLen;\n    }\n\n    @Override\n    public synchronized long getPos() throws IOException {\n      if (buf == null)\n        throw new IOException(\"stream was closed\");\n      return position - buf.remaining();\n    }\n\n    @Override\n    public boolean seekToNewSource(long targetPos) throws IOException {\n      return false;\n    }\n\n    @Override\n    public synchronized int available() throws IOException {\n      if (buf == null)\n        throw new IOException(\"stream was closed\");\n      long remaining = fileLen - position + buf.remaining();\n      if (remaining > Integer.MAX_VALUE) {\n        return Integer.MAX_VALUE;\n      }\n      return (int)remaining;\n    }","sourceCodeStart":1057,"sourceCodeEnd":1093,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java#L1057-L1093","documentation":"FSDataInputStream.getPos() on JuiceFileSystemImpl.FileInputStream throws IOException(\"stream was closed\") once the stream has been closed. Internally the buffered read implementation nulls its ByteBuffer in close(); any subsequent getPos() sees buf == null and throws rather than returning a stale position. This enforces the Hadoop FSInputStream contract that a closed stream is unusable.","triggerScenarios":"Calling in.getPos() after in.close() — e.g. a finally block that closes the stream and then logs/reports progress using getPos(), or a background progress thread still polling a stream another thread closed.","commonSituations":"MapReduce/Spark task cleanup ordering issues; double-close of FSDataInputStream in application code; reading statistics after try-with-resources has closed the stream.","solutions":["Capture getPos() before closing the stream.","Remove/stop any code that touches the stream after close() (progress reporters, logging callbacks).","Track closed state in the caller (boolean flag set at close) and skip stream operations afterwards."],"exampleFix":"// before\nlong pos = in.getPos();\nin.close();\nLOG.info(\"stopped at \" + in.getPos());\n// after\nlong pos = in.getPos();\nin.close();\nLOG.info(\"stopped at \" + pos);","handlingStrategy":"try-catch","validationCode":"boolean streamUsable = (in != null); // track close() yourself; set false in finally","typeGuard":null,"tryCatchPattern":"try {\n  long pos = in.getPos();\n} catch (IOException e) {\n  if (String.valueOf(e.getMessage()).contains(\"stream was closed\")) {\n    // stream already closed; use last recorded position\n  } else {\n    throw e;\n  }\n}","preventionTips":["Read position before close() and store it in a variable.","Use try-with-resources so close ordering is explicit and single.","Stop progress/monitor threads before closing streams."],"tags":["java","hadoop","stream-closed","use-after-close"],"backgroundTag":"stream-already-closed","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}