{"record":{"id":"06c1ae3b797b9b51","repo":"gitbutlerapp/gitbutler","slug":"installation-failed-and-backup-restoration-also-fa-06c1ae","errorCode":null,"errorMessage":"Installation failed and backup restoration also failed - 'but' command may not work","messagePattern":"Installation failed and backup restoration also failed - 'but' command may not work","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":273,"sourceCode":"        }\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\");\n                bail!(\"Installation failed but your previous installation was restored\");\n            } else {\n                bail!(\n                    \"Installation failed and backup restoration also failed - 'but' command may not work\"\n                );\n            }\n        } else {\n            bail!(\"Installation failed and no backup available to restore\");\n        }\n    }\n\n    success(&format!(\n        \"{} installed successfully\",\n        app_basename.to_string_lossy()\n    ));\n    // Remove backup on success\n    let _ = fs::remove_dir_all(&install_app_backup);\n\n    success(\"GitButler CLI (but) installed successfully\");\n\n    Ok(())","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-installer/src/install_macos.rs#L255-L291","documentation":"Critical macOS rollback failure: final validation of the new install failed, the backup app was moved back, but the restored setup also fails validation - or one of the restore steps itself errored, since the symlink recreate uses '?'. Both new and old binaries now fail to execute, pointing at a system-level execution problem rather than a bad artifact.","triggerScenarios":"Gatekeeper/MDM policy now blocks both binaries (quarantine re-applied, policy tightened mid-update); the OS lost runtime pieces an update removed; the restored symlink could not be recreated because the link path became unwritable mid-flight.","commonSituations":"macOS security update re-evaluating existing binaries; MDM pushing a blocking policy during the update window; disk or permission corruption.","solutions":["Test both directly: /Applications/GitButler.app/Contents/MacOS/gitbutler-tauri --version and ~/.local/bin/but --version; read the concrete error","If the symlink is broken, recreate it: ln -sf /Applications/GitButler.app/Contents/MacOS/gitbutler-tauri ~/.local/bin/but","Clear quarantine on the restored app: xattr -dr com.apple.quarantine /Applications/GitButler.app, then re-validate","If binaries still will not run, fix the OS-level cause (Gatekeeper allow, MDM exemption) before reinstalling"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Probe that both the app binary and the bin dir are usable before updating\nlet app_ok = std::process::Command::new(\n        install_app.join(\"Contents/MacOS/gitbutler-tauri\"))\n    .arg(\"--version\").stdout(std::process::Stdio::null())\n    .stderr(std::process::Stdio::null()).status()\n    .is_ok_and(|s| s.success());\nanyhow::ensure!(app_ok, \"existing app binary no longer executes; fix before updating\");\nstd::fs::create_dir_all(home.join(\".local/bin\"))?;","typeGuard":null,"tryCatchPattern":"if let Err(e) = but_installer::run_installation_with_version(request, false) {\n    if e.to_string().contains(\"backup restoration also failed\") {\n        // drive manual repair: recreate symlink, clear quarantine, reinstall\n        std::os::unix::fs::symlink(\n            install_app.join(\"Contents/MacOS/gitbutler-tauri\"),\n            home.join(\".local/bin/but\"))?;\n        let _ = std::process::Command::new(\"xattr\")\n            .args([\"-dr\", \"com.apple.quarantine\"]).arg(&install_app).status();\n    }\n    return Err(e);\n}","preventionTips":["Verify the existing app binary still executes before starting an update","Keep ~/.local/bin writable and unblocked by policy","On managed Macs, exempt GitButler binaries from Gatekeeper kills"],"tags":["rust","macos","rollback-failed","binary-validation","gatekeeper","but-installer"],"backgroundTag":"installation-rollback","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}