{"record":{"id":"31bcae047970ad0f","repo":"gitbutlerapp/gitbutler","slug":"failed-to-create-symlink-e-attempting-to-rest","errorCode":null,"errorMessage":"Failed to create symlink: {e} - attempting to restore backup","messagePattern":"Failed to create symlink: (.+?) - attempting to restore backup","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":235,"sourceCode":"            bail!(\n                \"Failed to install new version: {e}. Previous installation restored successfully.\"\n            );\n        }\n        // No backup to restore, just fail\n        bail!(\"Failed to move new installation into place: {e}\");\n    }\n\n    // Update the symlink to point to the new installation\n    let final_target = install_app.join(\"Contents/MacOS/gitbutler-tauri\");\n    let _ = fs::remove_file(&but_symlink);\n\n    // Try to create symlink and verify - if either fails, rollback\n    let symlink_result = unix_fs::symlink(&final_target, &but_symlink);\n    let _ = fs::remove_file(&but_new);\n\n    if let Err(e) = symlink_result {\n        // Symlink creation failed - rollback to backup\n        warn(&format!(\n            \"Failed to create symlink: {e} - attempting to restore backup\"\n        ));\n        if install_app_backup.exists() {\n            if let Err(remove_err) = fs::remove_dir_all(&install_app) {\n                bail!(\n                    \"Failed to create symlink and failed to remove new installation during rollback: {}. Backup at: {}\",\n                    remove_err,\n                    install_app_backup.display()\n                );\n            }\n            fs::rename(&install_app_backup, &install_app)?;\n\n            let restored_target = install_app.join(\"Contents/MacOS/gitbutler-tauri\");\n            let _ = fs::remove_file(&but_symlink);\n            let _ = unix_fs::symlink(&restored_target, &but_symlink);\n\n            bail!(\"Failed to create symlink: {e}. Previous installation was restored.\");\n        } else {","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-installer/src/install_macos.rs#L217-L253","documentation":"Warning emitted in the final swap phase when unix_fs::symlink(final_target, ~/.local/bin/but) fails after the new app bundle was already moved into place (install_macos.rs:230-237). The installer then rolls back: removes the new bundle, renames GitButler.app.backup back over ~/Applications/GitButler.app, re-creates the old symlink, and bails with 'Failed to create symlink: {e}. Previous installation was restored.' Note the preceding let _ = fs::remove_file(&but_symlink) discards removal errors, so a leftover directory at that path surfaces here as EEXIST/IsADirectory rather than as a clean removal failure.","triggerScenarios":"~/.local/bin/but is a directory (fs::remove_file cannot remove directories, so symlink creation fails with 'File exists'/'Is a directory'); permission denied on ~/.local/bin; read-only filesystem or full disk; another process recreating the path mid-install.","commonSituations":"A directory named 'but' created by other tooling in ~/.local/bin; ~/.local/bin owned by root after sudo-assisted setup; sandboxed installers without write access; endpoint-security agents locking the path.","solutions":["Inspect the blocking path: ls -ld ~/.local/bin/but - if it is a directory, remove it (rm -rf ~/.local/bin/but) and re-run the installer","Fix ownership and writability: chown -R $(whoami) ~/.local/bin && chmod u+w ~/.local/bin","Check disk and mount state: df -h ~ (the rollback renames need space and a writable volume)","Confirm the rollback left a working CLI: but --version; the bail message states the previous installation was restored, so nothing is lost","Re-run the installer once the path is clear"],"exampleFix":"# before\nls -ld ~/.local/bin/but\ndrwxr-xr-x  2 me staff  64 but    # directory blocks symlink creation -> EEXIST\n\n# after\nrm -rf ~/.local/bin/but\n# re-run installer; swap + symlink succeed, no rollback needed","handlingStrategy":"validation","validationCode":"# Before installing, make the swap target unobstructed:\n[ -d ~/.local/bin/but ] && rm -rf ~/.local/bin/but   # a directory is the classic blocker\n[ -e ~/.local/bin/but ] && [ ! -L ~/.local/bin/but ] && mv ~/.local/bin/but /tmp/but.saved\n[ -w ~/.local/bin ] && [ -x ~/.local/bin ] || chmod u+rwx ~/.local/bin\ndf -h ~ | tail -1   # renames need space on the same volume","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never create a directory named exactly 'but' in ~/.local/bin","Keep ~/.local/bin user-owned and writable; check 'ls -ld ~/.local/bin' after any sudo-assisted setup","If the failure fires anyway, trust but verify the rollback: run 'but --version' and only reinstall after clearing the blocking path"],"tags":["macos","installer","symlink","rollback","filesystem","permissions"],"backgroundTag":"symlink-creation-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}