jdx/mise · error
too many symbolic links in managed directory {}
Error message
too many symbolic links in managed directory {} What it means
A symlink-loop / symlink-depth guard in open_or_create_directory_tree_inner: if resolving a managed directory path requires following more than 40 symlinks, the operation aborts. This prevents infinite loops caused by cyclic symlinks and bounds worst-case traversal. Without it, a malicious or broken symlink cycle could hang the operation.
Source
Thrown at src/system/managed_files.rs:1582
/// Open an absolute directory path one component at a time without following
/// symlinks, creating missing components with process-default metadata. The
/// returned descriptor binds later metadata changes to the directory that was
/// actually opened instead of resolving the path again.
#[cfg(unix)]
fn open_or_create_directory_tree(path: &Path) -> Result<std::os::fd::OwnedFd> {
open_or_create_directory_tree_inner(path, 0)
}
#[cfg(unix)]
fn open_or_create_directory_tree_inner(
path: &Path,
followed_symlinks: usize,
) -> Result<std::os::fd::OwnedFd> {
use nix::fcntl::{AtFlags, OFlag, open, openat};
use nix::sys::stat::{Mode, SFlag, fstat, fstatat, mkdirat};
if followed_symlinks > 40 {
bail!(
"too many symbolic links in managed directory {}",
path.display()
);
}
let components = path
.strip_prefix(Path::new("/"))
.wrap_err_with(|| format!("managed directory must be absolute: {}", path.display()))?
.components()
.map(|component| match component {
std::path::Component::Normal(name) => Ok(name.to_os_string()),
_ => bail!("invalid managed directory path: {}", path.display()),
})
.collect::<Result<Vec<_>>>()?;
let flags = OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW;
let mut directory = open(Path::new("/"), flags, Mode::empty())?;
let mut current = PathBuf::from("/");View on GitHub (pinned to afd2eddd3a)
Solutions
- Find the loop with `namei -l <path>` or `readlink -f <path>` and remove the cyclic symlink
- Recreate the affected symlinks pointing at the real target
- Reinstall/recreate the managed directory from a clean state
- Audit directories on PATH of managed locations for unexpected symlinks
Example fix
// before $ ls -l /opt/tools/current current -> latest; latest -> current // loop // after $ rm /opt/tools/latest $ ln -s v1.2.3 /opt/tools/latest
Defensive patterns
Strategy: validation
Validate before calling
fn symlink_chain_length(path: &Path, max: usize) -> std::io::Result<usize> {
let mut current = path.to_path_buf();
let mut count = 0;
while let Ok(target) = std::fs::read_link(¤t) {
count += 1;
if count > max { return Err(std::io::Error::new(std::io::ErrorKind::FilesystemLoop, "too many symlinks")); }
current = if target.is_absolute() { target } else { current.parent().unwrap().join(target) };
}
Ok(count)
} Type guard
fn has_symlink_loop(path: &Path) -> bool {
std::fs::read_link(path).is_ok() && std::fs::canonicalize(path).is_err()
} Try / catch
match result {
Err(e) if e.to_string().contains("too many symbolic links") => diagnose_and_break_loop(path),
Err(e) => return Err(e),
Ok(v) => v,
} Prevention
- Avoid symlink chains; point links directly at real targets
- Audit dotfile-manager output for nested symlinks
- Use readlink -f / namei to validate paths after restores
- Keep managed directories under controlled, non-shared prefixes
When it happens
Trigger: The managed directory path contains components that are symlinks whose targets chain (or loop) more than 40 links deep — e.g. `a -> b -> c -> ... -> a`, or deeply chained symlinks accumulated over time.
Common situations: Symlink loops created by misconfigured dotfile managers (stow, chezmoi, GNU stow conflicts); backup/restore tools that mangled symlinks; attacker-planted loops in a shared/world-writable directory.
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 resolve managed directory {} to the filesystem r
- global configuration directory must not be a symlink
- brew-cask: staged symlink path escaped extraction root: {}
- brew-cask: refusing generic artifact source outside the extr
- failed to create file symlink: {err}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/15da9ede819ae376.
Report an issue: GitHub.