{"record":{"id":"727bdbd7a67b076d","repo":"apache/hadoop","slug":"stream-is-closed-727bdb","errorCode":null,"errorMessage":"Stream is closed!","messagePattern":"Stream is closed!","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-tos/src/main/java/org/apache/hadoop/fs/tosfs/object/ObjectMultiRangeInputStream.java","lineNumber":206,"sourceCode":"      stream.seek(nextPos);\n    }\n  }\n\n  private void closeStream(boolean asyncClose) throws IOException {\n    if (stream != null) {\n      if (asyncClose) {\n        final ObjectRangeInputStream streamToClose = stream;\n        threadPool.submit(() -> CommonUtils.runQuietly(streamToClose::close));\n      } else {\n        stream.close();\n      }\n      stream = null;\n    }\n  }\n\n  private void checkNotClosed() throws IOException {\n    if (closed.get()) {\n      throw new IOException(FSExceptionMessages.STREAM_IS_CLOSED);\n    }\n  }\n\n  @Override\n  public synchronized void close() throws IOException {\n    super.close();\n    if (closed.compareAndSet(false, true)) {\n      closeStream(false);\n    }\n  }\n\n  // for test\n  public long nextExpectPos() {\n    return currPos;\n  }\n\n  @Override\n  public synchronized int available() throws IOException {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-tos/src/main/java/org/apache/hadoop/fs/tosfs/object/ObjectMultiRangeInputStream.java#L188-L224","documentation":"ObjectMultiRangeInputStream tracks closure with an AtomicBoolean; once close() has run, the read/seek/positioned-read paths funnel through checkNotClosed(), which throws IOException(\"Stream is closed!\") from FSExceptionMessages.STREAM_IS_CLOSED. This is the standard Hadoop use-after-close guard for the multi-range object reader (which may also close its active range stream asynchronously via a thread pool).","triggerScenarios":"read(), seek(), or read(position, ...) invoked after close(); a cleanup path closing the stream while another thread still reads it; double usage from finally blocks or metric/log hooks that read after closing.","commonSituations":"Cleanup code in finally that closes the stream, followed by another component reading it; sharing one FSDataInputStream across tasks/threads without coordination; wrappers caching the inner stream beyond the owner's lifetime.","solutions":["Own the stream in exactly one place and use try-with-resources so reads cannot happen after close","Null out the reference after close and check it before each use","Coordinate concurrent close/read with a single owner thread or an external lock","During cancellation paths, treat 'Stream is closed!' as an expected benign signal rather than an error"],"exampleFix":"// before\nFSDataInputStream in = fs.open(path);\n// ... later, after in.close() somewhere\nin.read(buf); // IOException: Stream is closed!\n\n// after\ntry (FSDataInputStream in = fs.open(path)) {\n  in.read(buf);\n} // reads cannot outlive the stream","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  in.read(buf);\n} catch (IOException e) {\n  if (FSExceptionMessages.STREAM_IS_CLOSED.equals(e.getMessage())) {\n    // stream was closed (e.g. cancellation): stop reading, do not retry\n    return;\n  }\n  throw e;\n}","preventionTips":["Use try-with-resources so reads cannot outlive the stream","Keep single ownership; never share an FSDataInputStream across threads without coordination","Null out references after close and check before use","During cancellation, treat 'Stream is closed!' as expected"],"tags":["hadoop-tos","object-storage","input-stream","use-after-close","lifecycle"],"backgroundTag":"stream-already-closed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}