apache/hadoop · error · PathIsNotEmptyDirectoryException

iip.getPath() + " is non empty"

Error message

iip.getPath() + " is non empty"

What it means

FSDirDeleteOp.delete checks isNonEmptyDirectory(iip) and, when the delete is non-recursive (recursive=false from FileSystem.delete(path, false) or 'hdfs dfs -rm' without -r), throws PathIsNotEmptyDirectoryException. HDFS deliberately refuses to silently discard a populated directory: a non-recursive delete only removes empty directories and files named exactly by the path. If recursive=true is passed, the same directory deletes fine (after a protected-descendants check).

Source

Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirDeleteOp.java:111

   * @throws IOException
   */
  static BlocksMapUpdateInfo delete(
      FSNamesystem fsn, FSPermissionChecker pc, String src, boolean recursive,
      boolean logRetryCache) throws IOException {
    FSDirectory fsd = fsn.getFSDirectory();

    if (FSDirectory.isExactReservedName(src)) {
      throw new InvalidPathException(src);
    }

    final INodesInPath iip = fsd.resolvePath(pc, src, DirOp.WRITE_LINK);
    if (fsd.isPermissionEnabled()) {
      fsd.checkPermission(pc, iip, false, null, FsAction.WRITE, null,
                          FsAction.ALL, true);
    }
    if (fsd.isNonEmptyDirectory(iip)) {
      if (!recursive) {
        throw new PathIsNotEmptyDirectoryException(
            iip.getPath() + " is non empty");
      }
      DFSUtil.checkProtectedDescendants(fsd, iip);
    }

    return deleteInternal(fsn, iip, logRetryCache);
  }

  /**
   * Delete a path from the name space
   * Update the count at each ancestor directory with quota
   * <br>
   * Note: This is to be used by
   * {@link org.apache.hadoop.hdfs.server.namenode.FSEditLog} only.
   * <br>
   *
   * @param fsd the FSDirectory instance
   * @param iip inodes of a path to be deleted

View on GitHub (pinned to 2add963021)

Solutions

  1. If the intent is to remove everything, pass recursive=true: fs.delete(dir, true).
  2. If the intent is 'delete only when empty', keep recursive=false and catch PathIsNotEmptyDirectoryException as the expected signal (via RemoteException.unwrap / checking the exception class name).
  3. Alternatively list the directory first and decide: fs.listStatus(dir).length == 0 before deleting non-recursively.

Example fix

// before
fs.delete(dir, false); // throws if dir has children

// after
boolean removed;
if (fs.getFileStatus(dir).isDirectory() && fs.listStatus(dir).length > 0) {
  removed = fs.delete(dir, true);  // deliberate recursive delete
} else {
  removed = fs.delete(dir, false);
}
Defensive patterns

Strategy: validation

Validate before calling

boolean empty = fs.getFileStatus(dir).isDirectory()
    ? fs.listStatus(dir).length == 0 : true;
boolean removed = fs.delete(dir, /*recursive=*/ !empty);

Try / catch

try {
  fs.delete(dir, false);
} catch (PathIsNotEmptyDirectoryException e) {
  // only thrown client-side as RemoteException; unwrap first:
  // IOException ioe = RemoteExceptionHandler.decode(...)  (catch IOException, check className)
  // deliberate: directory was populated between check and delete
  fs.delete(dir, true);
}

Prevention

When it happens

Trigger: fs.delete(dirPath, false) where dirPath contains children; 'hdfs dfs -rm /some/dir' (without -r or -R) on a populated directory.

Common situations: Code ported from java.io.File.delete() or POSIX unlink() semantics where an explicit non-recursive delete is used as a safety guard; cleanup scripts that expect dirs to already be empty; tooling that intends 'delete only if empty' but does not handle the exception.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/4a8a39e6a62500c7. Report an issue: GitHub.