{"record":{"id":"bcdb977dbcbd6cb0","repo":"apache/hadoop","slug":"stream-is-closed-bcdb97","errorCode":null,"errorMessage":"Stream is closed!","messagePattern":"Stream is closed!","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java","lineNumber":1648,"sourceCode":"      seek(curPos+n);\n      return n;\n    }\n    return n < 0 ? -1 : 0;\n  }\n\n  /**\n   * Seek to a new arbitrary location\n   */\n  @Override\n  public synchronized void seek(long targetPos) throws IOException {\n    if (targetPos > getFileLength()) {\n      throw new EOFException(\"Cannot seek after EOF\");\n    }\n    if (targetPos < 0) {\n      throw new EOFException(\"Cannot seek to negative offset\");\n    }\n    if (closed.get()) {\n      throw new IOException(\"Stream is closed!\");\n    }\n    boolean done = false;\n    if (pos <= targetPos && targetPos <= blockEnd) {\n      //\n      // If this seek is to a positive position in the current\n      // block, and this piece of data might already be lying in\n      // the TCP buffer, then just eat up the intervening data.\n      //\n      int diff = (int)(targetPos - pos);\n      if (diff <= blockReader.available()) {\n        try {\n          pos += blockReader.skip(diff);\n          if (pos == targetPos) {\n            done = true;\n          } else {\n            // The range was already checked. If the block reader returns\n            // something unexpected instead of throwing an exception, it is\n            // most likely a bug.","sourceCodeStart":1630,"sourceCodeEnd":1666,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java#L1630-L1666","documentation":"DFSInputStream.seek(long) checks the stream's closed flag (an AtomicBoolean set by close()) and throws IOException('Stream is closed!') if the stream was already closed. A DFSInputStream is single-use: once closed (explicitly, via DFSClient shutdown, or by an internal fatal error), any seek on it fails. The check deliberately runs after the EOF/negative-offset checks, so a valid position on a closed stream is what produces this exact message.","triggerScenarios":"Calling seek() after close() returned, e.g. a finally block or cleanup task that repositions a stream already closed by try-with-resources. Also: seeking a stream obtained from FileSystem.open() whose DFSClient was shut down (clientRunning=false closes cached streams), or a cached/pooled FSDataInputStream that a previous request closed.","commonSituations":"Stream caching layers (Spark executors, Hive/Impala session caches, homegrown connection pools) that close one stream but keep handing it out; retry loops that close the stream on error then seek before reopening; lambdas/Runnables capturing a stream whose owner already exited its try-with-resources block.","solutions":["Reorder the lifecycle: perform all seeks and reads before close(), or reopen with fs.open(path) and seek on the new instance.","If streams are cached, invalidate the cache entry at close time so a stale closed stream is never handed out again.","Track the open/closed state alongside the stream handle in your wrapper class and fail with your own descriptive error before delegating to seek().","In retry logic, always reopen the stream (fs.open) rather than reusing the instance that was closed after a failure."],"exampleFix":"// before\ntry (FSDataInputStream in = fs.open(path)) {\n  parseHeader(in);\n} // try-with-resources closes here\ncache.setLastOffset(in.getPos()); // later code calls in.seek(offset) on closed stream\n\n// after\nlong lastOffset;\ntry (FSDataInputStream in = fs.open(path)) {\n  parseHeader(in);\n  lastOffset = in.getPos();\n}\n// when needed again:\ntry (FSDataInputStream in = fs.open(path)) {\n  in.seek(lastOffset);\n  parseBody(in);\n}","handlingStrategy":"try-catch","validationCode":"try {\n  in.available(); // cheap probe: throws IOException only when the DFS stream is closed\n} catch (IOException closed) {\n  in = fs.open(path); // reopen and reposition\n  in.seek(offset);\n}","typeGuard":null,"tryCatchPattern":"try {\n  in.seek(offset);\n} catch (IOException e) {\n  if (String.valueOf(e.getMessage()).contains(\"Stream is closed\")) {\n    in = fs.open(path); // single recovery: reopen, reposition, continue\n    in.seek(offset);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Scope each FSDataInputStream with try-with-resources around ALL of its use, including final seeks.","Never cache FSDataInputStream across requests without an eviction-on-close policy.","In retry logic, always fs.open(path) a fresh stream instead of reusing a closed one.","Keep the stream owner single-threaded or make close() the sole responsibility of one owner."],"tags":["hdfs","hdfs-client","stream-lifecycle","seek","resource-management"],"backgroundTag":"stream-already-closed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}