{"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/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/install_macos.rs#L217-L253","documentation":"After moving the new app bundle into place, install_app attempts unix_fs::symlink(final_target, but_symlink). If symlink creation fails, the installer warns with the OS error and enters rollback: it restores the backed-up previous installation (install_app_backup) if present, otherwise bails. The message means the atomic install could not finish and the previous state is being recovered.","triggerScenarios":"download_and_install_app -> install_app on macOS when creating the 'but' symlink fails, typically because the bin directory is not writable (permissions, SIP-protected paths, the path is a directory rather than a symlink) or the target file does not exist.","commonSituations":"Installing into /usr/local/bin without sudo on a locked-down Mac; corporate MDM policies blocking symlink creation in PATH dirs; a leftover 'but' directory occupying the symlink path; disk full.","solutions":["Re-run the installer with elevated privileges (sudo) or point it at a user-writable bin directory on PATH.","Check what occupies the symlink path: `ls -la $(dirname $(which but))/but` and remove any conflicting directory/file.","Verify free disk space and that the app bundle target (Contents/MacOS/gitbutler-tauri) exists in the installed .app.","Check the printed error after 'Failed to create symlink:' for the underlying errno and fix accordingly (e.g. chown the bin dir)."],"exampleFix":"# before\n$ ./gitbutler-installer   # fails: permission denied on /usr/local/bin\n# after\n$ sudo ./gitbutler-installer   # or set install dir to ~/bin (on PATH)","handlingStrategy":"validation","validationCode":"# Verify the bin dir is writable and the path is not occupied before installing.\ntest -w /usr/local/bin || echo 'need sudo or a different install dir'\n[ -d /usr/local/bin/but ] && echo \"conflict: /usr/local/bin/but is a directory\"","typeGuard":null,"tryCatchPattern":"// Wrap the installer run; treat non-zero exit as rollback failure candidate.\nconst res = spawnSync(installer, { stdio: 'inherit' });\nif (res.status !== 0) {\n  console.error('Install failed; check rollback note and permissions on the target bin dir.');\n}","preventionTips":["Install into a user-writable PATH directory (~/bin) when you lack sudo.","Ensure the symlink destination path is not already a directory or immutable file.","Keep disk space above a safe margin; symlink/bundle moves fail on a full disk.","On managed Macs, pre-approve installer permissions with MDM."],"tags":["symlink","installer","macos","filesystem","rollback"],"backgroundTag":"file-write-failed","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}