jdx/mise · error · eyre::Report
created path component
Error message
created path component {} was replaced before it could be opened What it means
Mise creates a missing path component with O_CREAT|O_EXCL-style semantics and then fstat()s it. If the freshly created component's owner is not the current effective UID, the path was swapped out between creation and open — a classic TOCTOU attack — so mise refuses to open the replaced component it just created.
Solutions
- Re-run the mise command — transient races usually succeed on retry
- Ensure no concurrent user/process is manipulating the same path
- Make the parent directory owned by you and not writable by others (chmod go-w)
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
import os
def component_owned_by_me(path):
return os.stat(path).st_uid == os.geteuid() Try / catch
match result {
Err(e) if e.to_string().contains("was replaced before it could be opened") => {
// transient race: back off briefly and retry once
}
r => r,
} Prevention
- Run only one mise instance per data directory at a time
- Avoid sharing mise paths between multiple Unix users
- Disable aggressive cleanup daemons on mise data directories
- Keep parent dirs writable only by the current user
When it happens
Trigger: Another process (or attacker) replaces a component mise just created in a managed path before mise opens it; created_by_us is true and the fstat uid check fails.
Common situations: Two users sharing a directory where one races the other's mise operation; a tmpwatch/cleanup daemon deleting and recreating directories mid-run; malicious symlink-swapping in a writable parent.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- brew-cask: refusing elevated operation because target…
- brew-cask: refusing operation through a changed generic…
- brew-cask: refusing operation through a changed generic…
- brew-cask: temporary artifact directory was replaced
- a parent directory is now a symlink; left untouched
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/c98e067e03ffbb17.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/managed_files.rs:1735
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 533346cc37)