uutils/coreutils · error · io::Error

path component exists but is not a directory: {}

Error message

path component exists but is not a directory: {}

What it means

In safe traversal, `open_or_create_subdir` inspects each path component's file type. If the component exists but is neither a directory nor a symlink, it returns `AlreadyExists` with "path component exists but is not a directory: {name}", because the traversal expected to descend into (or create) a directory there.

Source

Thrown at src/uucore/src/lib/features/safe_traversal.rs:586

/// * `parent_fd` - The parent directory file descriptor
/// * `name` - The name of the subdirectory to open or create
/// * `mode` - The mode to use when creating a new directory
///
/// # Returns
/// A DirFd for the subdirectory
fn open_or_create_subdir(parent_fd: &DirFd, name: &OsStr, mode: u32) -> io::Result<DirFd> {
    match parent_fd.stat_at(name, SymlinkBehavior::NoFollow) {
        Ok(stat) => {
            let file_type = (stat.st_mode as libc::mode_t) & libc::S_IFMT;
            match file_type {
                libc::S_IFDIR => parent_fd.open_subdir(name, SymlinkBehavior::NoFollow),
                libc::S_IFLNK => {
                    // Follow symlinks to directories (GNU coreutils behavior).
                    // O_DIRECTORY in open_subdir ensures we only succeed if the
                    // symlink resolves to a directory; dangling or non-dir symlinks error out.
                    parent_fd.open_subdir(name, SymlinkBehavior::Follow)
                }
                _ => Err(io::Error::new(
                    io::ErrorKind::AlreadyExists,
                    format!(
                        "path component exists but is not a directory: {}",
                        name.display()
                    ),
                )),
            }
        }
        Err(e) if e.kind() == io::ErrorKind::NotFound => match parent_fd.mkdir_at(name, mode) {
            Ok(()) => parent_fd.open_subdir(name, SymlinkBehavior::NoFollow),
            // Another process created `name` between the stat and the mkdir
            // (issue #12355). Open what it created; O_NOFOLLOW keeps a symlink
            // that raced into the name from being followed.
            Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
                parent_fd.open_subdir(name, SymlinkBehavior::NoFollow)
            }
            Err(e) => Err(e),
        },

View on GitHub (pinned to 85295bbf78)

Solutions

  1. Remove/rename the non-directory component so the directory can be created
  2. Use a different target path
  3. Check the existing path type and reconcile it before traversal

Example fix

# before
mkdir -p out/data   # out/data is a regular file
# after
mv out/data out/data.bak && mkdir -p out/data
Defensive patterns

Strategy: validation

Validate before calling

for ancestor in target.ancestors().skip(1) {
    if ancestor.exists() && !ancestor.is_dir() {
        return Err(format!("{} exists and is not a directory", ancestor.display()));
    }
}

Type guard

fn component_is_dir(p: &Path) -> bool {
    p.symlink_metadata().map(|m| m.is_dir()).unwrap_or(false)
}

Try / catch

match create_dir_all_safe(&target) {
    Err(e) if e.kind() == io::ErrorKind::AlreadyExists
        && e.to_string().contains("not a directory") => {
        eprintln!("{} blocks directory creation", target.display());
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `create_dir_all_safe` on a path where an intermediate component is a regular file, FIFO, socket, etc. (e.g. path `a/b/c` where `a/b` is a regular file).

Common situations: A file was created where a directory was expected, leftover state from a previous run, typo'd paths colliding with an existing file name.

Related errors


AI-assisted analysis of uutils/coreutils@85295bbf78 (2026-08-31). Data as JSON: /api/errors/93c9fbb2e0e0e8fb. Report an issue: GitHub.