{"record":{"id":"bd6735b69f5eabac","repo":"gitbutlerapp/gitbutler","slug":"failed-to-create-symlink-e-previous-installati","errorCode":null,"errorMessage":"Failed to create symlink: {e}. Previous installation was restored.","messagePattern":"Failed to create symlink: (.+?)\\. Previous installation was restored\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":252,"sourceCode":"        // 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 {\n            bail!(\"Failed to create symlink: {e}. No backup available to restore.\");\n        }\n    }\n\n    if !validate_installed_binary(&but_symlink) {\n        // Try to restore backup\n        warn(\"Final installation verification failed - attempting to restore backup\");\n        if install_app_backup.exists() {\n            fs::remove_dir_all(&install_app)?;\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            unix_fs::symlink(&restored_target, &but_symlink)?;\n\n            if validate_installed_binary(&but_symlink) {\n                success(\"Backup was restored successfully\");","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/install_macos.rs#L234-L270","documentation":"Creating the `but` symlink failed after the new app was installed; the installer successfully rolled back by removing the new install, renaming the backup into place, and re-pointing the symlink at the restored app. The bail reports the original symlink error and confirms the previous installation is back.","triggerScenarios":"install_app when unix_fs::symlink(&final_target, &but_symlink) fails while a backup exists - e.g. ~/.local/bin/but exists as a regular file or directory (EEXIST/ENOTDIR), or the bin dir is read-only (EPERM).","commonSituations":"User previously managed `but` with Homebrew or a manually placed binary at ~/.local/bin/but; PATH management tools recreated the file; ~/.local/bin owned by root after an earlier sudo install.","solutions":["Check what occupies ~/.local/bin/but (`file ~/.local/bin/but`) and remove/rename the non-symlink entry, then retry","Fix permissions on ~/.local/bin (`sudo chown -R $(whoami) ~/.local/bin`)","If Homebrew provides `but`, uninstall that formula first (`brew uninstall but`)","Retry the install - the previous installation was restored, so it is safe"],"exampleFix":"// before\nlet _ = fs::remove_file(&but_symlink);\nlet symlink_result = unix_fs::symlink(&final_target, &but_symlink);\n// after (caller-side precheck)\nlet meta = fs::symlink_metadata(bin_dir.join(\"but\"));\nif let Ok(m) = meta {\n    if !m.file_type().is_symlink() {\n        fs::rename(bin_dir.join(\"but\"), bin_dir.join(\"but.user-override\")).unwrap();\n    }\n}","handlingStrategy":"validation","validationCode":"let link = home.join(\".local/bin/but\");\nmatch fs::symlink_metadata(&link) {\n    Ok(m) if !m.file_type().is_symlink() => {\n        bail!(\"{} exists as a regular file/dir - move or remove it before install\", link.display());\n    }\n    Err(e) if e.kind() != std::io::ErrorKind::NotFound => {\n        bail!(\"cannot inspect {}: {e}\", link.display());\n    }\n    _ => {}\n}","typeGuard":"fn but_symlink_replaceable(path: &Path) -> bool {\n    fs::symlink_metadata(path)\n        .map(|m| m.file_type().is_symlink())\n        .unwrap_or(false)\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"Previous installation was restored\") => {\n        eprintln!(\"fix ~/.local/bin/but and retry; old install is intact: {e}\");\n    }\n    other => other?,\n}","preventionTips":["Do not install `but` via Homebrew or copy a binary to ~/.local/bin/but manually","Keep ~/.local/bin user-owned","Let the installer own the symlink; remove foreign `but` entries first","Re-run the installer after fixing PATH-managed conflicts"],"tags":["symlink","filesystem","rollback","installation"],"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"}