gitbutlerapp/gitbutler · error

Failed to move new installation into place

Error message

Failed to move new installation into place: {e}

What it means

The atomic swap rename of the new .app into place failed and there was no previous installation to restore (had_backup was false, i.e. no GitButler.app existed yet). The installer bails with just the underlying rename error, leaving the system without an installed app (stale artifacts were already cleaned up).

Solutions

  1. Remove any partial directory at the install path (`rm -rf` the leftover GitButler.app) and retry
  2. Check the destination is writable: `ls -ld` the install parent dir; fix ownership/permissions
  3. Free disk space (`df -h`) if the disk is full
  4. Ensure no process holds the destination path open, then rerun the installer

Example fix

// before: blindly retrying
installer::download_and_install_app(...)?;
// after: precheck target path is clear
let target = home.join("Applications/GitButler.app");
if target.exists() && !target.symlink_metadata()?.is_dir() {
    bail!("unexpected file at {} - remove it before installing", target.display());
}
Defensive patterns

Strategy: validation

Validate before calling

let target = home.join("Applications/GitButler.app");
if target.exists() {
    bail!("stale path {} blocks install - remove it first", target.display());
}
let test = home.join("Applications/.write-test");
fs::write(&test, b"x").map_err(|e| format!("install dir not writable: {e}"))?;
let _ = fs::remove_file(&test);

Try / catch

match result {
    Err(e) if e.to_string().contains("Failed to move new installation into place") => {
        eprintln!("check permissions/disk space at install path, then retry: {e}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: install_app (via download_and_install_app) on a fresh machine (no existing install) when fs::rename(&install_app_new, &install_app) fails: destination volume read-only, permissions problem, disk full, or a non-empty directory already occupies the target path from a partially deleted install.

Common situations: First-ever CLI install on a locked-down corporate Mac; ~/Applications is not writable; leftover partial GitButler.app directory from a crashed previous install blocks the rename.

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/a27d1d78b27f1bef. Report an issue: GitHub.

Appendix: source

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

        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!(
            "Failed to create symlink: {e} - attempting to restore backup"
        ));
        if install_app_backup.exists() {
            if let Err(remove_err) = fs::remove_dir_all(&install_app) {
                bail!(

View on GitHub (pinned to 58e5313667)