{"record":{"id":"364f74c055a07455","repo":"apache/hadoop","slug":"is-a-directory-364f74","errorCode":null,"errorMessage":"Is a directory","messagePattern":"Is a directory","errorType":"exception","errorClass":"PathIsDirectoryException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Delete.java","lineNumber":113,"sourceCode":"        return super.expandArgument(arg);\n      } catch (PathNotFoundException e) {\n        if (!ignoreFNF) {\n          throw e;\n        }\n        // prevent -f on a non-existent glob from failing\n        return Collections.emptyList();\n      }\n    }\n\n    @Override\n    protected void processNonexistentPath(PathData item) throws IOException {\n      if (!ignoreFNF) super.processNonexistentPath(item);\n    }\n\n    @Override\n    protected void processPath(PathData item) throws IOException {\n      if (item.stat.isDirectory() && !deleteDirs) {\n        throw new PathIsDirectoryException(item.toString());\n      }\n\n      // TODO: if the user wants the trash to be used but there is any\n      // problem (ie. creating the trash dir, moving the item to be deleted,\n      // etc), then the path will just be deleted because moveToTrash returns\n      // false and it falls thru to fs.delete.  this doesn't seem right\n      if (moveToTrash(item) || !canBeSafelyDeleted(item)) {\n        return;\n      }\n      if (!item.fs.delete(item.path, deleteDirs)) {\n        throw new PathIOException(item.toString());\n      }\n      out.println(\"Deleted \" + item);\n    }\n\n    private boolean canBeSafelyDeleted(PathData item)\n        throws IOException {\n      boolean shouldDelete = true;","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Delete.java#L95-L131","documentation":"PathIsDirectoryException ('Is a directory') thrown by Rm.processPath (Delete.java:113) when 'hadoop fs -rm' (without -r/-R) targets an existing directory. The shell deliberately mirrors POSIX rm: deleting a directory requires the recursive flag, which sets deleteDirs=true and skips this guard.","triggerScenarios":"'hadoop fs -rm /data/somedir' where the path is a directory; scripts that delete a path assuming it is a file but the path is (or has become) a parent directory; output dirs left by a previous job.","commonSituations":"Re-running a cleanup step after a job recreated a directory; parameterizing 'hadoop fs -rm $PATH' where $PATH sometimes points at a directory; porting 'rm -rf' habits without the -r flag.","solutions":["If recursive delete is intended: 'hadoop fs -rm -r /data/somedir'","If only files should go: expand to files explicitly, e.g. 'hadoop fs -rm \"/data/somedir/*\"'","Pre-check with 'hadoop fs -test -d /path' in scripts and branch to -rm -r","In the Java API use fs.delete(path, true) for directories"],"exampleFix":"# before\nhadoop fs -rm /data/somedir     # Is a directory\n\n# after\nhadoop fs -rm -r /data/somedir","handlingStrategy":"validation","validationCode":"FileStatus st = fs.getFileStatus(path);\nboolean recursive = st.isDirectory();\nfs.delete(path, recursive);","typeGuard":"static boolean isDirectory(FileSystem fs, Path p) throws IOException {\n  return fs.getFileStatus(p).isDirectory();\n}","tryCatchPattern":"try {\n  fs.delete(path, false);\n} catch (PathIsDirectoryException e) {\n  fs.delete(path, true); // escalate to recursive only if intended\n}","preventionTips":["Use 'hadoop fs -test -d $P' in scripts to pick -rm vs -rm -r","Default to -r in cleanup jobs only where recursive delete is explicitly safe","In the Java API pass the recursive flag from a stat check, not a hardcoded value"],"tags":["hadoop","rm","shell","path-is-directory","argument-validation"],"backgroundTag":"path-is-directory","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}