{"record":{"id":"4a142a7196c46893","repo":"apache/hadoop","slug":"stream-is-closed-4a142a","errorCode":null,"errorMessage":"{}: Stream is closed!","messagePattern":"(.+?): Stream is closed!","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSInputStream.java","lineNumber":752,"sourceCode":"    long endTime = System.currentTimeMillis();\n    LOG.debug(\n        \"Read-3args uri:{}, contentLength:{}, destLen:{}, readLen:{}, \"\n            + \"position:{}, thread:{}, timeUsedMilliSec:{}\",\n        uri, contentLength, len, bytesRead,\n        bytesRead >= 0 ? nextReadPos - bytesRead : nextReadPos, threadId,\n        endTime - startTime);\n    return bytesRead;\n  }\n\n  /**\n   * Verify that the input stream is open. Non blocking; this gives the last\n   * state of the volatile {@link #closed} field.\n   *\n   * @throws IOException if the connection is closed.\n   */\n  private void checkNotClosed() throws IOException {\n    if (closed) {\n      throw new IOException(\n          uri + \": \" + FSExceptionMessages.STREAM_IS_CLOSED);\n    }\n  }\n\n  /**\n   * Close the stream. This triggers publishing of the stream statistics back to\n   * the filesystem statistics. This operation is synchronized, so that only one\n   * thread can attempt to close the connection; all later/blocked calls are\n   * no-ops.\n   *\n   * @throws IOException on any problem\n   */\n  @Override\n  public synchronized void close() throws IOException {\n    if (!closed) {\n      closed = true;\n      // close or abort the stream\n      closeStream(\"close() operation\", this.contentRangeFinish);","sourceCodeStart":734,"sourceCodeEnd":770,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSInputStream.java#L734-L770","documentation":"OBSInputStream.checkNotClosed() guards every positional operation (seek, read, readFully, getPos when invoked via those paths): once the volatile closed flag is set by close(), any further operation throws IOException(uri + ': ' + STREAM_IS_CLOSED). The uri prefix distinguishes this from the block-buffer variant. Closing is synchronized and idempotent ('all later/blocked calls are no-ops'), so the error always means the caller used the stream after close — never a close-side race.","triggerScenarios":"Calling read()/seek()/readFully() on an FSDataInputStream wrapping OBSInputStream after close(); try-with-resources scope ended but a cached reference is still used; another thread closed the stream (timeout handler, cancellation hook) while a reader continues; finally-block ordering that closes the stream before a last read.","commonSituations":"Wrapping the stream in a cached reader (BufferedReader, DataInputStream) and reading past the owning try block; async pipelines where a supervisor closes streams on error but workers keep polling; double-managed lifecycle — both framework and user code closing, then user code re-reading.","solutions":["Keep all reads strictly inside the try-with-resources (or try/finally) block that owns the stream","Centralize ownership: exactly one component closes the stream; readers check a shared closed/lifecycle flag before I/O","On cancellation paths, signal readers to stop before closing their streams (CountDownLatch/AtomicBoolean), then close","Wrap OBS reads in a guard: if (in instanceof FSDataInputStream) use available()/getPos() carefully — but the reliable fix is lifecycle discipline, not probing state"],"exampleFix":"// before\nFSDataInputStream in = fs.open(path);\n... in.close();\nreturn in.read(); // IOException: s3a://...: Stream is closed!\n\n// after\ntry (FSDataInputStream in = fs.open(path)) {\n  return readAll(in);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return in.read(buf, off, len);\n} catch (IOException e) {\n  if (String.valueOf(e.getMessage()).endsWith(FSExceptionMessages.STREAM_IS_CLOSED)) {\n    // lifecycle bug: reading after close — stop, never retry with the same handle\n    throw new IllegalStateException(\"stream used after close\", e);\n  }\n  throw e;\n}","preventionTips":["Single owner closes; readers stop before close is called","try-with-resources for every OBS input stream","On cancellation, signal readers first (AtomicBoolean/latch), then close streams"],"tags":["stream-lifecycle","io","state-violation","hadoop-obs"],"backgroundTag":"io-stream-closed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}