juicedata/juicefs · error · java.io.IOException

errno: ${errno} ${path} (via error(); maps errno to PathPerm

Error message

errno: ${errno} ${path} (via error(); maps errno to PathPermissionException/FileNotFoundException/AccessControlException/etc.)

What it means

The Java SDK's delete() calls the native jfs_delete; any negative errno other than ENOENT is converted by error(errno, p) into the matching Hadoop IOException subclass (PathPermissionException for EPERM, FileNotFoundException for ENOENT, AccessControlException for EACCES, etc.). The thrown exception carries 'errno: <n> <path>'. This is the standard error-mapping surface for delete failures.

Source

Thrown at sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java:1731

      try {
        if (!checkParentPathAccess(p, FsAction.WRITE_EXECUTE, "delete")) {
          return superGroupFileSystem.delete(p, recursive);
        }
      } catch (Exception e) {
        if (!checkPathAccess(p, FsAction.WRITE_EXECUTE, "delete")) {
          return superGroupFileSystem.delete(p, recursive);
        }
      }
    }
    statistics.incrementWriteOps(1);
    if (recursive)
      return rmr(p);
    int r = lib.jfs_delete(Thread.currentThread().getId(), handle, normalizePath(p));
    if (r == ENOENT) {
      return false;
    }
    if (r < 0) {
      throw error(r, p);
    }
    return true;
  }

  @Override
  public ContentSummary getContentSummary(Path f) throws IOException {
    if (needCheckPermission() && !checkPathAccess(f, FsAction.READ_EXECUTE, "getContentSummary")) {
      return superGroupFileSystem.getContentSummary(f);
    }
    statistics.incrementReadOps(1);
    String path = normalizePath(f);
    Pointer buf = Memory.allocate(Runtime.getRuntime(lib), 40);
    int r = lib.jfs_summary(Thread.currentThread().getId(), handle, path, buf);
    if (r < 0) {
      throw error(r, f);
    }
    long size = buf.getLongLong(0);
    long files = buf.getLongLong(8);

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Check parent-directory write permission for the current Hadoop user (owner/group/mode) before delete
  2. Use recursive=true for non-empty directories to avoid ENOTEMPTY
  3. Verify the path string and that the volume is not mounted read-only
  4. Catch and inspect the specific Hadoop exception subclass to branch on permission vs not-found

Example fix

// before
fs.delete(path, false); // may throw PathIsNotEmptyDirectoryException
// after
if (fs.exists(path)) {
  fs.delete(path, true); // recursive
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Java
Path parent = path.getParent();
FsPermission perm = fs.getFileStatus(parent).getPermission();
boolean canWrite = perm.applyUMask(FsPermission.getUMask(null)).implies(FsAction.WRITE);
if (!canWrite) throw new AccessControlException("no write on " + parent);

Try / catch

try {
  fs.delete(p, recursive);
} catch (PathPermissionException | AccessControlException e) {
  LOG.warn("delete denied: {}", e.getMessage());
} catch (FileNotFoundException e) {
  // already gone — treat as success
}

Prevention

When it happens

Trigger: `fs.delete(path, recursive)` where the native layer returns EPERM/EACCES (no write permission on parent), EINVAL, ENOTEMPTY (non-recursive delete of non-empty dir), EROFS, etc.

Common situations: Deleting a file owned by another user without permissions on the parent directory; deleting a non-empty directory with recursive=false; operating on a read-only mounted volume; path does not exist (handled as 'return false' for ENOENT, not thrown).

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/1bf7b0e6bea01962. Report an issue: GitHub.