{"record":{"id":"e4ecbd2b4400f19c","repo":"jdx/mise","slug":"too-many-symlinks-while-resolving-atomic-write-tar","errorCode":null,"errorMessage":"too many symlinks while resolving atomic write target: {}","messagePattern":"too many symlinks while resolving atomic write target: (.+?)","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/file.rs","lineNumber":519,"sourceCode":"\n    let mut target = path.to_path_buf();\n    for followed in 0..=MAX_SYMLINKS {\n        if !target.is_symlink() {\n            return Ok(desymlink_path(&target));\n        }\n        if followed == MAX_SYMLINKS {\n            break;\n        }\n        let link = fs::read_link(&target)\n            .wrap_err_with(|| format!(\"failed to read symlink: {}\", display_path(&target)))?;\n        target = if link.is_absolute() {\n            link\n        } else {\n            target.parent().unwrap_or_else(|| Path::new(\"\")).join(link)\n        };\n    }\n\n    bail!(\n        \"too many symlinks while resolving atomic write target: {}\",\n        display_path(path)\n    )\n}\n\nfn persist_atomic(mut temporary: tempfile::NamedTempFile, path: &Path) -> Result<()> {\n    const RETRIES: u32 = 20;\n\n    for attempt in 0..=RETRIES {\n        match temporary.persist(path) {\n            Ok(_) => return Ok(()),\n            Err(err) if should_retry_atomic_persist(&err.error) && attempt < RETRIES => {\n                temporary = err.file;\n                std::thread::sleep(Duration::from_millis(5 * u64::from(attempt + 1)));\n            }\n            Err(err) => return Err(err.error.into()),\n        }\n    }","sourceCodeStart":501,"sourceCodeEnd":537,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/file.rs#L501-L537","documentation":"Atomic writes in src/file.rs resolve the destination through symlinks before persisting a temp file, following at most `MAX_SYMLINKS` links. Hitting the limit means the chain is a symlink loop (a -> b -> a) or an absurdly long chain; writing through it is unsafe and would never terminate, so the write bails naming the original path.","triggerScenarios":"Any mise atomic state/config write (settings, lockfile, cache) whose target path traverses a cyclic symlink chain — typically a self-referential or mutually-referential symlink inside the mise data/install directories, e.g. an install path alias pointing back into another alias of the same version.","commonSituations":"Hand-made symlinks sharing installs between version aliases that ended up cyclic; interrupted installs leaving half-replaced links; pointing MISE_DATA_PATH into a directory tree that symlinks back into itself; aggressive dotfile/sync setups linking cache dirs in a loop.","solutions":["Walk the path from the message with `readlink` repeatedly to find where the loop closes","Remove the offending symlink(s) (`rm <link>`, never `rm -r`) or repoint them at the real target directory","Reinstall the affected tool version to rebuild clean state: `mise install -f <tool>@<version>`","Prefer mise's own alias mechanism over manual install-dir symlinks, and keep MISE_DATA_PATH free of cyclic links"],"exampleFix":"# before: installs/node/22 -> installs/node/default -> installs/node/22\n$ rm ~/.local/share/mise/installs/node/default\n$ mise alias set node 22 default   # managed alias, no symlink loop","handlingStrategy":"try-catch","validationCode":"# detect symlink loops in mise dirs before they break writes\np=\"$HOME/.local/share/mise\"\nfor i in $(seq 1 41); do\n  p=$(readlink \"$p\" 2>/dev/null) || break\n  [ -n \"$p\" ] || break\ndone","typeGuard":null,"tryCatchPattern":"Catch write errors containing `too many symlinks while resolving atomic write target`; resolve and delete the cyclic link, then retry the mise command once — repeated failures mean the loop is elsewhere in the chain.","preventionTips":["Use mise's alias mechanism instead of hand-rolling install-dir symlinks","Keep MISE_DATA_PATH on plain directories, never inside symlink-cyclic layouts","After restoring from backup, verify mise install dirs contain no unexpected links (`find ~/.local/share/mise -type l`)"],"tags":["mise","filesystem","symlink","atomic-write"],"backgroundTag":"symlink-loop","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}