gitbutlerapp/gitbutler · error
Failed to install new version
Error message
Failed to install new version: {e}. Previous installation restored successfully. What it means
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.
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
Example fix
// before (user script ignoring the failure)
let res = installer::download_and_install_app(...);
// after
match res {
Err(e) if e.to_string().contains("restored successfully") => {
eprintln!("Install failed but old app intact; kill running GitButler and retry: {e}");
}
other => other?,
} Defensive patterns
Strategy: retry
Validate before calling
// Before install: ensure no running GitButler and target volume is writable
use std::process::Command;
let running = Command::new("pgrep")
.args(["-f", "gitbutler"])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
assert!(!running, "GitButler is running; quit it before installing");
assert!(fs::metadata(install_parent)?.permissions().readonly() == false, "install dir not writable"); Try / catch
match installer::download_and_install_app(...) {
Err(e) if e.to_string().contains("restored successfully") => {
// old install intact: safe to kill processes and retry
eprintln!("retry install after closing GitButler: {e}");
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Failed to create symlink
- Failed to move new installation into place
- Failed to create symlink and failed to remove new…
- Failed to create symlink
- Failed to create symlink
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/591fc85aa32dbe13.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-installer/src/install_macos.rs:217
}
// Move new installation into place with rollback on failure
if let Err(e) = fs::rename(&install_app_new, &install_app) {
// Clean up stale artifacts before handling the error
cleanup_artifacts();
// Critical failure - restore backup if we created one
if had_backup && install_app_backup.exists() {
warn("Failed to move new installation into place - restoring backup");
if let Err(restore_err) = fs::rename(&install_app_backup, &install_app) {
bail!(
"Failed to install new version: {}. Also failed to restore backup: {}. Your app may be at {}",
e,
restore_err,
install_app_backup.display()
);
}
bail!(
"Failed to install new version: {e}. Previous installation restored successfully."
);
}
// No backup to restore, just fail
bail!("Failed to move new installation into place: {e}");
}
// Update the symlink to point to the new installation
let final_target = install_app.join("Contents/MacOS/gitbutler-tauri");
let _ = fs::remove_file(&but_symlink);
// Try to create symlink and verify - if either fails, rollback
let symlink_result = unix_fs::symlink(&final_target, &but_symlink);
let _ = fs::remove_file(&but_new);
if let Err(e) = symlink_result {
// Symlink creation failed - rollback to backup
warn(&format!(View on GitHub (pinned to 58e5313667)