{"record":{"id":"591fc85aa32dbe13","repo":"gitbutlerapp/gitbutler","slug":"failed-to-install-new-version-e-previous-insta","errorCode":null,"errorMessage":"Failed to install new version: {e}. Previous installation restored successfully.","messagePattern":"Failed to install new version: (.+?)\\. Previous installation restored successfully\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":217,"sourceCode":"    }\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\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!(","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/install_macos.rs#L199-L235","documentation":"During a GitButler CLI (but) macOS self-install, the atomic swap step (fs::rename of the freshly downloaded .app into the install location) failed. The installer attempted to roll back by renaming the previous installation from its backup path, and that rollback succeeded, so the user's old app is intact. The bail() carries both the underlying rename error and the reassurance that the restore worked.","triggerScenarios":"install_app (via download_and_install_app) when: had_backup was true (an existing GitButler.app existed), fs::rename(&install_app_new, &install_app) failed (e.g. destination locked, cross-device rename, target on read-only volume), and the subsequent fs::rename(&install_app_backup, &install_app) succeeded.","commonSituations":"Another GitButler process is running and holds the app bundle open; the install volume (~/.local or ~/Applications on a different mount) is read-only or full; a directory exists at the destination that blocks the rename; disk permission issues after a macOS update.","solutions":["Quit all GitButler/but processes (Activity Monitor or `pkill -f gitbutler`) and retry the install","Check free disk space and that the install volume is writable (`df -h ~`, `ls -ld ~/Applications`)","Run the installer again; the previous installation was restored, so a clean retry is safe","If rename keeps failing across mount points, ensure the download temp dir and install dir are on the same volume"],"exampleFix":"// before (user script ignoring the failure)\nlet res = installer::download_and_install_app(...);\n// after\nmatch res {\n    Err(e) if e.to_string().contains(\"restored successfully\") => {\n        eprintln!(\"Install failed but old app intact; kill running GitButler and retry: {e}\");\n    }\n    other => other?,\n}","handlingStrategy":"retry","validationCode":"// Before install: ensure no running GitButler and target volume is writable\nuse std::process::Command;\nlet running = Command::new(\"pgrep\")\n    .args([\"-f\", \"gitbutler\"])\n    .output()\n    .map(|o| o.status.success())\n    .unwrap_or(false);\nassert!(!running, \"GitButler is running; quit it before installing\");\nassert!(fs::metadata(install_parent)?.permissions().readonly() == false, \"install dir not writable\");","typeGuard":null,"tryCatchPattern":"match installer::download_and_install_app(...) {\n    Err(e) if e.to_string().contains(\"restored successfully\") => {\n        // old install intact: safe to kill processes and retry\n        eprintln!(\"retry install after closing GitButler: {e}\");\n    }\n    other => other?,\n}","preventionTips":["Always quit GitButler before running a CLI update","Keep download temp dir and install dir on the same volume so rename is atomic","Monitor disk space before updates","Parse the message for 'restored successfully' to know the rollback succeeded before retrying"],"tags":["filesystem","installation","rollback","rename-failed"],"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"}