apache/seatunnel · error · IOException

Directory: %s is not empty.

Error message

Directory: %s is not empty.

What it means

delete() refuses to remove a directory that still contains entries when recursive=false, throwing IOException formatted with E_DIR_NOTEMPTY. This mirrors Hadoop FileSystem semantics: non-recursive deletes of non-empty directories are rejected rather than silently truncating data.

Source

Thrown at seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPFileSystem.java:383

            fileStat = getFileStatus(channel, absolute);
        } catch (FileNotFoundException e) {
            // file not found, no need to delete, return true
            return false;
        }
        if (!fileStat.isDirectory()) {
            boolean status = true;
            try {
                channel.rm(pathName);
            } catch (SftpException e) {
                status = false;
            }
            return status;
        } else {
            boolean status = true;
            FileStatus[] dirEntries = listStatus(channel, absolute);
            if (dirEntries != null && dirEntries.length > 0) {
                if (!recursive) {
                    throw new IOException(String.format(E_DIR_NOTEMPTY, file));
                }
                for (int i = 0; i < dirEntries.length; ++i) {
                    delete(channel, new Path(absolute, dirEntries[i].getPath()), recursive);
                }
            }
            try {
                channel.rmdir(pathName);
            } catch (SftpException e) {
                status = false;
            }
            return status;
        }
    }

    /**
     * Convenience method, so that we don't open a new connection when using this method from within
     * another method. Otherwise every API invocation incurs the overhead of opening/closing a TCP
     * connection.

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Pass recursive=true: fs.delete(path, true)
  2. Empty the directory first (delete each child) then delete the directory
  3. Pick a fresh output path or enable overwrite-aware cleanup before the job
  4. Verify the directory is actually empty (fs.listStatus(path).length == 0) before a non-recursive delete

Example fix

// before
fs.delete(new Path("/tmp/out"), false);
// after
fs.delete(new Path("/tmp/out"), true);
Defensive patterns

Strategy: validation

Validate before calling

Path dir = new Path("/tmp/out");
if (fs.exists(dir) && fs.getFileStatus(dir).isDirectory()
        && fs.listStatus(dir).length > 0 && !recursive) {
    throw new IllegalStateException("Refusing non-recursive delete of non-empty " + dir);
}

Try / catch

try {
    fs.delete(dir, false);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).contains("is not empty")) {
        fs.delete(dir, true); // escalate to recursive
    } else { throw e; }
}

Prevention

When it happens

Trigger: fs.delete(dirPath, false) on a directory that contains at least one file or subdirectory (as reported by listStatus).

Common situations: Cleanup code assuming delete() clears whole trees but passing recursive=false; overwriting an existing non-empty output directory during job setup; leftover files from a previous failed run.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/a4b9f2e4f0935db9. Report an issue: GitHub.