apache/hadoop · error · InvalidRequestException
Bucket %s cannot be deleted
Error message
Bucket %s cannot be deleted
What it means
OBSCommonUtils.blockRootDelete(bucket, key) is a guard invoked before object deletes: if the decoded key is empty or exactly '/' — i.e. the caller asked to delete the bucket ROOT object 'obs://bucket/' — it throws InvalidRequestException('Bucket <bucket> cannot be deleted'). The connector refuses to issue a delete that would target the root of the bucket namespace, because that maps to deleting every object or the bucket itself depending on SDK semantics.
Source
Thrown at hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSCommonUtils.java:345
ioe = new OBSIOException(message, exception);
break;
}
return ioe;
}
/**
* Reject any request to delete an object where the key is root.
*
* @param bucket bucket name
* @param key key to validate
* @throws InvalidRequestException if the request was rejected due to a
* mistaken attempt to delete the root
* directory.
*/
static void blockRootDelete(final String bucket, final String key)
throws InvalidRequestException {
if (key.isEmpty() || "/".equals(key)) {
throw new InvalidRequestException(
"Bucket " + bucket + " cannot be deleted");
}
}
/**
* Delete an object. Increments the {@code OBJECT_DELETE_REQUESTS} and write
* operation statistics.
*
* @param owner the owner OBSFileSystem instance
* @param key key to blob to delete.
* @throws IOException on any failure to delete object
*/
static void deleteObject(final OBSFileSystem owner, final String key)
throws IOException {
blockRootDelete(owner.getBucket(), key);
ObsException lastException = null;
for (int retryTime = 1; retryTime <= MAX_RETRY_TIME; retryTime++) {
try {View on GitHub (pinned to 2add963021)
Solutions
- Guard your loop: stop deleting parent dirs when path depth == bucket root (path.isRoot() or path.getParent() == null).
- If you truly want to empty a bucket, use delete(new Path("obs://bucket/"), true) with recursive=true — that path is governed by rejectRootDirectoryDelete, not this object-level guard — or list-and-delete keys explicitly.
- Fix key arithmetic: never produce empty keys; assert !key.isEmpty() before calling delete APIs.
- Review distcp -p and 'delete empty dirs' style options when the working root is the bucket itself.
Example fix
// before
Path p = fileToDelete.getParent();
while (p != null) { fs.delete(p, false); p = p.getParent(); }
// last iteration p == obs://bucket/ -> 'Bucket ... cannot be deleted'
// after
Path p = fileToDelete.getParent();
while (p != null && !p.isRoot()) { fs.delete(p, false); p = p.getParent(); }
// stop above root; or explicitly: if (!p.isRoot()) fs.delete(p, false); Defensive patterns
Strategy: validation
Validate before calling
static boolean isBucketRoot(Path p) {
String path = p.toUri().getPath();
return path == null || path.isEmpty() || "/".equals(path);
}
// before delete:
if (isBucketRoot(target)) throw new IllegalArgumentException("refusing root delete: " + target); Type guard
static Path safeObjectPath(Path p) {
String k = p.toUri().getPath();
if (k == null || k.isEmpty() || "/".equals(k))
throw new IllegalArgumentException("bucket-root paths are not deletable objects: " + p);
return p;
} Try / catch
try {
fs.delete(objPath, false);
} catch (InvalidRequestException e) {
if (String.valueOf(e.getMessage()).contains("cannot be deleted")) {
LOG.error("bug: attempted root delete for {}", objPath);
}
throw e;
} Prevention
- Stop parent-cleanup loops at path.isRoot().
- Compute keys with library helpers; assert non-empty before delete.
- Config-lint base-dir settings so the 'root' of cleanup is never the bucket itself.
When it happens
Trigger: delete(path) where path.toUri().getPath() decodes to '/' or '' — e.g. new Path("obs://mybucket/") or new Path("obs://mybucket"); code that computes key = parent - child and ends with the empty string; rename/cleanup logic that deletes the 'parent directory' after emptying it and walks all the way to root.
Common situations: Recursive cleanup scripts that delete parents after removing children and forget to stop at the bucket root; temp-dir cleanup where the temp root was configured as the bucket itself; renames computing destination keys with string arithmetic that yields empty; distcp jobs asked to 'remove empty source dirs' where source is the root.
Related errors
- Cannot delete root path
- Proxy error: %s or %s set without the other.
- From option %s %s
- Filesystem %s closed
- write has error. bs : pre upload obs[%s] has error.
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/f5f1b9ace8dfe1b4.
Report an issue: GitHub.