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's create_dir_all_safe, open_or_create_subdir walks path components using directory file descriptors. When an existing component's file type is neither a directory nor a symlink, it returns AlreadyExists with 'path component exists but is not a directory: {name}' to mirror GNU coreutils' mkdir -p behavior.

Solutions

  1. Remove or rename the file occupying the component path
  2. Use a different target directory
  3. Check each component with metadata/symlink_metadata before creating

Example fix

// before
create_dir_all_safe("out/data") // out is a regular file
// after
if fs::symlink_metadata("out")?.is_file() { fs::remove_file("out")?; }
create_dir_all_safe("out/data");
Defensive patterns

Strategy: validation

Validate before calling

fn component_is_dir(p: &Path) -> bool {
    std::fs::metadata(p).map(|m| m.is_dir()).unwrap_or(true) // missing => ok to create
}

Type guard

fn is_dir_component(p: &Path) -> Option<bool> {
    std::fs::symlink_metadata(p).ok().map(|m| m.is_dir())
}

Try / catch

match create_dir_all_safe(path) {
    Err(e) if e.to_string().contains("exists but is not a directory") => {
        eprintln!("remove or rename the file occupying {path:?}");
    }
    r => r?,
}

Prevention

When it happens

Trigger: create_dir_all_safe("a/b/c") where `a` or `a/b` exists as a regular file, fifo, socket, etc., so opening it as a subdirectory is impossible.

Common situations: A file shadowing a directory that a previous run created; build scripts leaving a marker file where a directory is expected; package installs conflicting with existing files.

Related errors


AI-assisted analysis of uutils/coreutils@92995feca2 (2026-09-20). Data as JSON: /api/errors/93c9fbb2e0e0e8fb. Report an issue: GitHub.

Appendix: source

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

/// * `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 92995feca2)