apache/hadoop · error · PathIOException
Cannot delete root path
Error message
Cannot delete root path
What it means
delete() on the bucket root throws PathIOException("Cannot delete root path") when a non-recursive delete is attempted on a non-empty root. This is deliberate: contract tests (AbstractContractRootDirectoryTest#testRmNonEmptyRootDirNonRecursive) require an exception here, while recursive delete of a non-empty root returns false (testRmRootRecursive) and an empty root returns true.
Source
Thrown at hadoop-cloud-storage-project/hadoop-tos/src/main/java/org/apache/hadoop/fs/tosfs/RawFileSystem.java:381
* @return true if root directory is empty, false if trying to delete a non-empty dir recursively.
* @throws IOException if trying to delete the non-empty root dir non-recursively.
*/
private boolean deleteRoot(Path root, boolean recursive) throws IOException {
LOG.info("Delete the {} root directory of {}", bucket, recursive);
boolean isEmptyDir = fsOps.isEmptyDirectory(root);
if (isEmptyDir) {
return true;
}
if (recursive) {
// AbstractContractRootDirectoryTest#testRmRootRecursive doesn't expect any exception if
// trying to delete a non-empty root directory recursively, so we have to return false here
// instead of throwing a IOException.
return false;
} else {
// AbstractContractRootDirectoryTest#testRmNonEmptyRootDirNonRecursive expect a exception if
// trying to delete a non-empty root directory non-recursively, so we have to throw a
// IOException instead of returning false.
throw new PathIOException(bucket, "Cannot delete root path");
}
}
@Override
public RawFileStatus[] listStatus(Path f) throws IOException {
LOG.debug("List status for path: {}", f);
return Iterators.toArray(listStatus(f, false), RawFileStatus.class);
}
public Iterator<RawFileStatus> listStatus(Path f, boolean recursive) throws IOException {
Path path = makeQualified(f);
// Assuming path is a dir at first.
Iterator<RawFileStatus> iterator = fsOps.listDir(path, recursive, key -> true).iterator();
if (iterator.hasNext()) {
return iterator;
} else {
RawFileStatus fileStatus = innerFileStatus(path);
if (fileStatus.isFile()) {View on GitHub (pinned to 2add963021)
Solutions
- Never delete the root; iterate fs.listStatus(root) and delete children explicitly
- If using delete(f, false) for empty-dir semantics, guard with a root check and skip or log instead
- Catch PathIOException for this specific case when generic cleanup code must tolerate it
Example fix
// before
fs.delete(new Path("/"), false); // non-empty bucket root -> PathIOException
// after
Path root = fs.getHomeDirectory().getParent(); // bucket root
if (!f.isRoot()) { fs.delete(f, recursive); }
else { for (FileStatus st : fs.listStatus(f)) { fs.delete(st.getPath(), true); } } Defensive patterns
Strategy: try-catch
Validate before calling
Path q = f.makeQualified(fs.getUri(), fs.getWorkingDirectory());
boolean isRoot = q.toUri().getPath().equals("/");
if (isRoot && !recursive) {
// skip or list-and-delete children instead
} Try / catch
try {
fs.delete(f, false);
} catch (PathIOException e) {
if ("Cannot delete root path".equals(e.getOperation())) { /* root guard hit: clean children instead */ }
else throw e;
} Prevention
- Never issue delete() on the qualified root path
- Guard cleanup loops with isRoot checks
- Remember contract semantics: recursive root delete returns false, non-recursive throws
When it happens
Trigger: fs.delete(new Path("/"), false) (recursive=false) when the bucket contains any objects; equivalently fs.delete(path-to-bucket-root, false) after qualification.
Common situations: Cleanup code that walks to the filesystem root and deletes it; tools that call delete(path, false) on directories expecting POSIX rmdir-of-empty behavior hitting a non-empty bucket; miscomputed paths that collapse to '/'.
Related errors
- Cannot rename the root directory %s to another name
- Directory {} is not empty.
- Can not delete root path
- Can not delete the directory: [%s], as it is not empty and o
- Deleting resource %s failed.
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/0ed8ff02a60b20b7.
Report an issue: GitHub.