jdx/mise · critical · eyre::Report
refusing to resolve managed directory
Error message
refusing to resolve managed directory {} to the filesystem root What it means
When following a symlink during managed-directory resolution, mise computes the fully resolved absolute path and refuses to continue if it lands on the filesystem root '/'. Following a chain of symlinks to '/' would make mise operate on the entire root filesystem, so it aborts instead of opening or creating directories there.
Solutions
- Inspect the symlink chain (readlink -f <path>) and fix the final target so it does not point at /
- Replace the offending symlink with a real directory
- Remove and recreate the managed path correctly
Example fix
// before ln -s / ~/.local/share/mise/data # resolves to root // after ln -s /data/mise ~/.local/share/mise/data
Defensive patterns
Strategy: validation
Validate before calling
import os
def resolves_to_root(p):
return os.path.realpath(p) == "/" Try / catch
if let Err(e) = open(path) {
if e.to_string().contains("filesystem root") {
// inspect readlink -f path and fix the symlink chain before retrying
}
} Prevention
- Never create symlinks pointing at / inside managed paths
- Check symlink chains with readlink -f before wiring up data directories
- Keep symlink targets inside the managed tree
- Verify directory layout after partial restores or rsync operations
When it happens
Trigger: A relative symlink target inside a managed path (or accumulation of resolved components via absolutize()) resolves to '/' exactly, causing open_or_create_directory_tree_inner to bail after the resolved == Path::new("/") check.
Common situations: A symlink like lib -> / or a symlink chain that walks up past the managed root; misconfigured dotfile symlinks pointing at /; recovering from a botched rm that replaced a directory with a link to /.
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
- refusing to follow symlink
- a parent directory is now a symlink; left untouched
- binary target is not an owned Caskroom symlink
- brew-cask: refusing to replace structured symlink directory
- changed during recovery; left untouched
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/646f235d128df753.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/managed_files.rs:1694
SFlag::from_bits_truncate(metadata.st_mode).contains(SFlag::S_IFLNK)
}) {
let parent = fstat(&directory)?;
if parent.st_uid != 0 || parent.st_mode & 0o022 != 0 {
bail!(
"refusing to follow symlink {} from an untrusted parent directory",
component_path.display()
);
}
let target = nix::fcntl::readlinkat(&directory, name.as_os_str())?;
let mut resolved = if Path::new(&target).is_absolute() {
PathBuf::from(target)
} else {
current.join(target)
};
resolved.extend(components.iter().skip(index + 1));
let resolved = resolved.absolutize()?.to_path_buf();
if resolved == Path::new("/") {
bail!(
"refusing to resolve managed directory {} to the filesystem root",
path.display()
);
}
return open_or_create_directory_tree_inner(&resolved, followed_symlinks + 1);
}
if open_error != nix::errno::Errno::ENOENT {
return Err(open_error).wrap_err_with(|| {
format!(
"failed to open path component {} without following symlinks",
component_path.display()
)
});
}
let created_by_us = match mkdirat(
&directory,
name.as_os_str(),
Mode::from_bits_truncate(0o777),View on GitHub (pinned to 533346cc37)