{"record":{"id":"473be5cfcd5c75eb","repo":"apache/hadoop","slug":"cannot-remove-directory","errorCode":null,"errorMessage":"Cannot remove directory {}","messagePattern":"Cannot remove directory (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BlockPoolSliceStorage.java","lineNumber":541,"sourceCode":"   * \n   * If the directory is not empty report an error; Otherwise remove the\n   * directory.\n   * \n   * @param detachDir detach directory\n   * @throws IOException if the directory is not empty or it can not be removed\n   */\n  private void cleanupDetachDir(File detachDir) throws IOException {\n    if (!DataNodeLayoutVersion.supports(\n        LayoutVersion.Feature.APPEND_RBW_DIR, layoutVersion)\n        && detachDir.exists() && detachDir.isDirectory()) {\n\n      if (FileUtil.list(detachDir).length != 0) {\n        throw new IOException(\"Detached directory \" + detachDir\n            + \" is not empty. Please manually move each file under this \"\n            + \"directory to the finalized directory if the finalized \"\n            + \"directory tree does not have the file.\");\n      } else if (!detachDir.delete()) {\n        throw new IOException(\"Cannot remove directory \" + detachDir);\n      }\n    }\n  }\n\n  /**\n   * Restore all files from the trash directory to their corresponding\n   * locations under current/\n   */\n  private int restoreBlockFilesFromTrash(File trashRoot)\n      throws  IOException {\n    int filesRestored = 0;\n    File[] children = trashRoot.exists() ? trashRoot.listFiles() : null;\n    if (children == null) {\n      return 0;\n    }\n\n    File restoreDirectory = null;\n    for (File child : children) {","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BlockPoolSliceStorage.java#L523-L559","documentation":"During the same legacy detach cleanup, the detached/ directory was empty but File.delete() returned false — the directory could not be removed, so the upgrade still fails. Usually a permissions/ownership problem, an open handle, or exotic filesystem (NFS) semantics.","triggerScenarios":"DN process lacks write permission on the parent of detached/; another process (or NFS client) holds the directory; read-only mount.","commonSituations":"Running the DN as a different user than the one owning the storage dirs; NAS-backed data dirs; containers with restricted mounts.","solutions":["Check ownership/permissions of detached/ and its parent (chown to the DN user)","Find and stop processes holding the directory (lsof), ensure the mount is read-write","Remove detached/ manually, then restart the upgrade"],"exampleFix":"chown -R hdfs:hdfs /dfs/dn/current/BP-*/detached\nrmdir /dfs/dn/current/BP-*/detached\nhdfs --daemon start datanode -upgrade","handlingStrategy":"try-catch","validationCode":"if (!detachDir.isDirectory() || Files.isWritable(detachDir.getParentFile().toPath())) {\n  // ok to attempt cleanup\n}","typeGuard":null,"tryCatchPattern":"catch (IOException e) {\n  if (e.getMessage().contains(\"Cannot remove directory\")) {\n    // permissions/handle issue: surface actionable context, do not loop retries\n    throw new IOException(\"Cannot remove \" + dir + \": check ownership/open handles\", e);\n  }\n}","preventionTips":["Run the DataNode as the user that owns the storage directories","Avoid NAS-backed data dirs where delete/rename semantics are unreliable","Stop scanners/backup agents touching DN storage during upgrades"],"tags":["hdfs","datanode","storage","filesystem","permissions"],"backgroundTag":"filesystem-permission-denied","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}