{"record":{"id":"8994d9e55d4c6bf5","repo":"gitbutlerapp/gitbutler","slug":"failed-to-install-new-version-also-failed-to","errorCode":null,"errorMessage":"Failed to install new version: {}. Also failed to restore backup: {}. Your app may be at {}","messagePattern":"Failed to install new version: (.+?)\\. Also failed to restore backup: (.+?)\\. Your app may be at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":210,"sourceCode":"\n    // Backup existing installation if it exists\n    let had_backup = install_app.exists();\n    if had_backup && let Err(e) = fs::rename(&install_app, &install_app_backup) {\n        // Failed to backup - clean up artifacts before failing\n        cleanup_artifacts();\n        return Err(e.into());\n    }\n\n    // Move new installation into place with rollback on failure\n    if let Err(e) = fs::rename(&install_app_new, &install_app) {\n        // Clean up stale artifacts before handling the error\n        cleanup_artifacts();\n\n        // Critical failure - restore backup if we created one\n        if had_backup && install_app_backup.exists() {\n            warn(\"Failed to move new installation into place - restoring backup\");\n            if let Err(restore_err) = fs::rename(&install_app_backup, &install_app) {\n                bail!(\n                    \"Failed to install new version: {}. Also failed to restore backup: {}. Your app may be at {}\",\n                    e,\n                    restore_err,\n                    install_app_backup.display()\n                );\n            }\n            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","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/install_macos.rs#L192-L228","documentation":"In the macOS installer's critical failure path, moving the new .app into place failed (`e`) and restoring the backed-up previous installation also failed (`restore_err`). The error chains both failures plus the backup location so the user can manually recover their previous installation.","triggerScenarios":"The rename of the staged new .app over the existing install fails (permissions, destination locked because the app is running, cross-volume rename) and the subsequent rename of the backup back to the install path also fails (same causes, or backup already moved/deleted).","commonSituations":"The GitButler app is running and macOS holds a lock on the bundle; installing from a context without write permission to /Applications; the target is on a different volume than the staging dir (APFS External/FAT USB); Finder/antivirus interfering mid-swap.","solutions":["Quit GitButler completely (check Activity Monitor for lingering processes) and re-run the installer.","Manually restore: move the backup at the path named in the error message back to the app's install location (e.g. /Applications/GitButler.app).","Ensure you have write permission to the install directory (install via sudo, or use a user Applications folder).","Re-run the installer afterwards to complete the upgrade once the destination is writable."],"exampleFix":"// before\nbut upgrade   # both install and restore failed\n// after\nosascript -e 'quit app \"GitButler\"'   # ensure app not running\nsudo mv \"$BACKUP_PATH\" /Applications/GitButler.app   # path from error message\nbut upgrade","handlingStrategy":"fallback","validationCode":"// ensure destination is on the same volume as staging and not currently in use\nlet running = std::process::Command::new(\"pgrep\")\n    .args([\"-x\", \"GitButler\"]).output()\n    .map(|o| !o.stdout.is_empty()).unwrap_or(false);\nif running { anyhow::bail!(\"GitButler is running; quit it before upgrading\"); }\n// check writability of the install parent (e.g. /Applications)\nlet probe = install_dir.join(\".probe\");\nstd::fs::write(&probe, b\"\").context(\"install directory not writable\")?;\nlet _ = std::fs::remove_file(&probe);","typeGuard":null,"tryCatchPattern":"match install_app(&config) {\n    Err(e) if e.to_string().contains(\"Your app may be at\") => {\n        let backup = extract_backup_path(&e.to_string());\n        eprintln!(\"restoring manually from {backup:?}\");\n        std::fs::rename(backup, \"/Applications/GitButler.app\")?;\n    }\n    r => r?,\n}","preventionTips":["Always quit the app before upgrading so macOS doesn't hold the bundle open.","Stage the new .app on the same volume as the destination so renames are atomic (same APFS volume).","Run the updater with rights to write the install directory (admin or user Applications dir).","Note the backup path from this error immediately — it is your recovery point for a manual restore."],"tags":["macos","installation","rollback-failed","backup"],"backgroundTag":"invalid-state-transition","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"}