{"record":{"id":"15da9ede819ae376","repo":"jdx/mise","slug":"too-many-symbolic-links-in-managed-directory","errorCode":null,"errorMessage":"too many symbolic links in managed directory {}","messagePattern":"too many symbolic links in managed directory (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":1582,"sourceCode":"/// Open an absolute directory path one component at a time without following\n/// symlinks, creating missing components with process-default metadata. The\n/// returned descriptor binds later metadata changes to the directory that was\n/// actually opened instead of resolving the path again.\n#[cfg(unix)]\nfn open_or_create_directory_tree(path: &Path) -> Result<std::os::fd::OwnedFd> {\n    open_or_create_directory_tree_inner(path, 0)\n}\n\n#[cfg(unix)]\nfn open_or_create_directory_tree_inner(\n    path: &Path,\n    followed_symlinks: usize,\n) -> Result<std::os::fd::OwnedFd> {\n    use nix::fcntl::{AtFlags, OFlag, open, openat};\n    use nix::sys::stat::{Mode, SFlag, fstat, fstatat, mkdirat};\n\n    if followed_symlinks > 40 {\n        bail!(\n            \"too many symbolic links in managed directory {}\",\n            path.display()\n        );\n    }\n\n    let components = path\n        .strip_prefix(Path::new(\"/\"))\n        .wrap_err_with(|| format!(\"managed directory must be absolute: {}\", path.display()))?\n        .components()\n        .map(|component| match component {\n            std::path::Component::Normal(name) => Ok(name.to_os_string()),\n            _ => bail!(\"invalid managed directory path: {}\", path.display()),\n        })\n        .collect::<Result<Vec<_>>>()?;\n\n    let flags = OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW;\n    let mut directory = open(Path::new(\"/\"), flags, Mode::empty())?;\n    let mut current = PathBuf::from(\"/\");","sourceCodeStart":1564,"sourceCodeEnd":1600,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/managed_files.rs#L1564-L1600","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n$ ls -l /opt/tools/current\ncurrent -> latest; latest -> current  // loop\n// after\n$ rm /opt/tools/latest\n$ ln -s v1.2.3 /opt/tools/latest","handlingStrategy":"validation","validationCode":"fn symlink_chain_length(path: &Path, max: usize) -> std::io::Result<usize> {\n    let mut current = path.to_path_buf();\n    let mut count = 0;\n    while let Ok(target) = std::fs::read_link(&current) {\n        count += 1;\n        if count > max { return Err(std::io::Error::new(std::io::ErrorKind::FilesystemLoop, \"too many symlinks\")); }\n        current = if target.is_absolute() { target } else { current.parent().unwrap().join(target) };\n    }\n    Ok(count)\n}","typeGuard":"fn has_symlink_loop(path: &Path) -> bool {\n    std::fs::read_link(path).is_ok() && std::fs::canonicalize(path).is_err()\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"too many symbolic links\") => diagnose_and_break_loop(path),\n    Err(e) => return Err(e),\n    Ok(v) => v,\n}","preventionTips":["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"],"tags":["filesystem","symlink","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}