jdx/mise · critical

created path component {} was replaced before it could be op

Error message

created path component {} was replaced before it could be opened

What it means

A race-condition check after creating a directory component with an exclusive open: the library fstats the freshly created fd and verifies it is owned by the current effective uid. If it is not, and this process created the component (created_by_us), some other actor replaced the path between creation and open. This detects symlink-swap attacks or concurrent interference on freshly created components.

Source

Thrown at src/system/managed_files.rs:1667

                        return Err(error).wrap_err_with(|| {
                            format!(
                                "failed to create path component {}",
                                component_path.display()
                            )
                        });
                    }
                };
                let created = openat(&directory, name.as_os_str(), flags, Mode::empty())
                    .wrap_err_with(|| {
                        format!(
                            "failed to open newly available path component {} without following symlinks",
                            component_path.display()
                        )
                    })?;
                let stat = nix::sys::stat::fstat(&created)?;
                if stat.st_uid != nix::unistd::geteuid().as_raw() {
                    if created_by_us {
                        bail!(
                            "created path component {} was replaced before it could be opened",
                            component_path.display()
                        );
                    } else {
                        bail!(
                            "path component {} was concurrently created by another user",
                            component_path.display()
                        );
                    }
                }
                created
            }
        };
        current.push(name);
    }
    Ok(directory)
}

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Ensure the managed prefix directory is only writable by the current user/root (chmod/chown the parent)
  2. Re-run the operation; transient races usually succeed once the path is settled
  3. Remove the path component that was tampered with and recreate it as the correct user
  4. Avoid sharing managed directories between users; give each user its own prefix

Example fix

// before
$ ls -ld /opt/tools  # drwxrwxrwx root root
// after
$ sudo chown root:root /opt/tools && sudo chmod 755 /opt/tools
Defensive patterns

Strategy: retry

Validate before calling

fn prefix_is_private(path: &Path) -> std::io::Result<bool> {
    use std::os::unix::fs::MetadataExt;
    let mut cur = path.to_path_buf();
    while let Some(parent) = cur.parent() {
        let m = std::fs::metadata(parent)?;
        if m.mode() & 0o022 != 0 { return Ok(false); }
        cur = parent.to_path_buf();
    }
    Ok(true)
}

Type guard

fn component_owned_by_euid(path: &Path) -> bool {
    use std::os::unix::fs::MetadataExt;
    std::fs::symlink_metadata(path)
        .map(|m| m.uid() == unsafe { libc::geteuid() })
        .unwrap_or(false)
}

Try / catch

for attempt in 0..3 {
    match result {
        Err(e) if e.to_string().contains("was replaced before it could be opened") && attempt < 2 => continue,
        Err(e) => return Err(e),
        Ok(v) => return Ok(v),
    }
}

Prevention

When it happens

Trigger: During open_or_create_directory_tree, a component was just created (O_EXCL-style) but the fd's st_uid differs from the process euid — meaning the path was replaced (e.g. by a symlink plant) between mkdir and open. Without created_by_us, a different message ('concurrently created by another user') is used instead.

Common situations: Local attacker racing a privileged mise operation in a shared directory; parallel package managers or build systems creating the same path concurrently as different users; container/image build steps running as mixed uids over the same prefix.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/c98e067e03ffbb17. Report an issue: GitHub.