gitbutlerapp/gitbutler · critical

Failed to move new installation into place - restoring…

Error message

Failed to move new installation into place - restoring backup

What it means

install_app on macOS moves the staged new .app bundle into /Applications with fs::rename. When that rename fails, the installer warns that it is restoring the backup of the previous installation, then tries rename(backup, install_app). If the restore also fails it bails with a combined message naming both errors and where the backup lives.

Solutions

  1. Quit the running app (and any helper processes) and retry the install.
  2. Re-run the installer with elevated privileges (sudo) so it can replace the bundle in /Applications.
  3. Check that the backup path printed in the bail message still contains your old app and restore it manually if needed.
  4. Ensure temp staging and /Applications are on the same volume, or copy instead of rename when crossing devices.
  5. Free disk space if the rename failed due to insufficient space during cross-device copy.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight checks before install
import subprocess, os
def can_install(app_path):
    if os.path.exists(app_path):
        try:
            os.rename(app_path, app_path + '.probe')
            os.rename(app_path + '.probe', app_path)
        except OSError as e:
            return False, f'Cannot rename target: {e}'
    return os.access(os.path.dirname(app_path), os.W_OK), None

Try / catch

// Rust pattern: check rename result and preserve backup path on failure
if let Err(e) = fs::rename(&install_app_new, &install_app) {
    if let Err(re) = fs::rename(&install_app_backup, &install_app) {
        bail!("install failed: {e}; restore failed: {re}; backup at {}", install_app_backup.display());
    }
}

Prevention

When it happens

Trigger: Raised in install_app (called from download_and_install_app) when fs::rename(&install_app_new, &install_app) fails — e.g. /Applications/App.app is locked by a running process, or the destination sits on a different volume so rename must copy cross-device and fails.

Common situations: The app is running and macOS holds the bundle open; permission issues on /Applications without admin rights; the target volume differs from the temp dir (cross-filesystem rename not supported); antivirus/quarantine tooling locking the bundle.

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


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/2770059f00144b18. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-installer/src/install_macos.rs:208

        let _ = fs::remove_file(&but_new);
    };

    // Backup existing installation if it exists
    let had_backup = install_app.exists();
    if had_backup && let Err(e) = fs::rename(&install_app, &install_app_backup) {
        // Failed to backup - clean up artifacts before failing
        cleanup_artifacts();
        return Err(e.into());
    }

    // 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");

View on GitHub (pinned to 58e5313667)