{"record":{"id":"c57818ce4b7a2a5b","repo":"apache/hadoop","slug":"filesystem-s-closed","errorCode":null,"errorMessage":"Filesystem %s closed","messagePattern":"Filesystem (.+?) closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSBlockOutputStream.java","lineNumber":255,"sourceCode":"\n  /**\n   * Clear the active block.\n   */\n  private synchronized void clearActiveBlock() {\n    if (activeBlock != null) {\n      LOG.debug(\"Clearing active block\");\n    }\n    activeBlock = null;\n  }\n\n  /**\n   * Check for the filesystem being open.\n   *\n   * @throws IOException if the filesystem is closed.\n   */\n  private void checkOpen() throws IOException {\n    if (closed.get()) {\n      throw new IOException(\n          \"Filesystem \" + writeOperationHelper.toString(key) + \" closed\");\n    }\n  }\n\n  /**\n   * The flush operation does not trigger an upload; that awaits the next block\n   * being full. What it does do is call {@code flush() } on the current block,\n   * leaving it to choose how to react.\n   *\n   * @throws IOException Any IO problem.\n   */\n  @Override\n  public synchronized void flush() throws IOException {\n    checkOpen();\n    OBSDataBlocks.DataBlock dataBlock = getActiveBlock();\n    if (dataBlock != null) {\n      dataBlock.flush();\n    }","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSBlockOutputStream.java#L237-L273","documentation":"OBSBlockOutputStream.checkOpen() throws IOException('Filesystem <writeOperationHelper.toString(key)> closed') when any write/flush operation is attempted after the owning OBSFileSystem instance was closed (AtomicBoolean 'closed' set). The stream keeps a reference to the filesystem's writeOperationHelper, so once fs.close() runs (e.g. at job teardown) every subsequent write on any still-open stream fails with this message — it is a lifecycle bug in the caller, not an OBS service error.","triggerScenarios":"Calling fs.close() in a finally block while background threads still write through cached output streams; caching an FSDataOutputStream across tasks and closing the FileSystem between them; Spark executor reuse where a UDF writes lazily after the cluster shuts the FS; Timer/async threads flushing metrics files post-shutdown.","commonSituations":"Static/shared FileSystem singletons closed by one task while another still writes; shutdown hooks racing appender threads; mapreduce task commit closing the FS before a slow record writer finishes; tests that close fs in @AfterEach while async writers linger.","solutions":["Fix ownership: close output streams BEFORE closing the FileSystem (try-with-resources nested correctly), and only close the FS when no writers remain.","For shared FileSystems use FileSystem.closeAllForUGI or reference-counted caches instead of manual fs.close() on a cached instance.","Cancel/join background writer threads before FS teardown (await termination of executors that touch the stream).","In Spark, prefer path-based lazy writes or re-obtain the FileSystem per task instead of caching across task lifecycle boundaries."],"exampleFix":"// before\nFSDataOutputStream out = fs.create(path);\nexecutor.submit(() -> { out.write(buf); });\nfs.close(); // writer thread now hits 'Filesystem ... closed'\n\n// after\nFSDataOutputStream out = fs.create(path);\nFuture<?> f = executor.submit(() -> { out.write(buf); });\nf.get(30, TimeUnit.SECONDS);\nout.close();\nfs.close(); // streams closed first, writers joined","handlingStrategy":"validation","validationCode":"boolean fsUsable = true; // track alongside every cached FileSystem\n// guard before each write batch:\nif (fsClosed) throw new IllegalStateException(\"filesystem already closed for \" + path);","typeGuard":null,"tryCatchPattern":"try {\n  out.write(buf, 0, n);\n} catch (IOException e) {\n  if (String.valueOf(e.getMessage()).endsWith(\" closed\") && e.getMessage().contains(\"Filesystem\")) {\n    throw new LifecycleException(\"Writer outlived OBSFileSystem — close streams before fs.close()\", e);\n  }\n  throw e;\n}","preventionTips":["Close in strict order: stream → filesystem; use nested try-with-resources.","Never close FileSystem instances you did not create (FileSystem.closeAllForUGI at job end instead).","Join background writer threads before FS teardown.","Audit static FS caches for double-close and cross-task reuse."],"tags":["obs","huaweicloud","stream-lifecycle","io","resource-management"],"backgroundTag":"stream-closed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}