gitbutlerapp/gitbutler · critical

Installation failed and backup restoration also failed - 'bu

Error message

Installation failed and backup restoration also failed - 'but' command may not work

What it means

Worst-case Linux rollback: the new binary failed post-install validation, the backup was renamed back into ~/.local/bin/but, but the restored binary also fails 'but --version'. The installer gives up and warns the but command may not work. This is a system-level execution problem, not a bad artifact - two independent binaries fail to run.

Source

Thrown at crates/but-installer/src/install_linux.rs:119

    // a different mount point than persistent files
    fs::copy(but_path, &install_bin_path)?;

    let mut perms = fs::metadata(&install_bin_path)?.permissions();
    perms.set_mode(0o755);
    fs::set_permissions(&install_bin_path, perms)?;

    if !validate_installed_binary(&install_bin_path) {
        warn("Final installation verification failed");

        if let Some(but_backup) = but_backup {
            warn("Attempting to restore backup");
            fs::rename(&but_backup, &install_bin_path)?;

            if validate_installed_binary(&install_bin_path) {
                info("Backup restored successfully, exiting ...");
                bail!("Installation failed but your previous installation was restored");
            } else {
                bail!(
                    "Installation failed and backup restoration also failed - 'but' command may not work"
                );
            }
        } else {
            bail!("Installation failed and no backup available to restore");
        }
    } else if let Some(but_backup) = but_backup {
        info(&format!(
            "Removing backup at {}",
            but_backup.to_string_lossy()
        ));
        fs::remove_file(&but_backup)?;
    }

    Ok(())
}

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Run ~/.local/bin/but --version directly and read the exact error (ENOEXEC, missing interpreter, 'Permission denied')
  2. Check for noexec: findmnt -T ~/.local/bin/but -o OPTIONS; remount without noexec or move the bin dir
  3. Restore endpoint-security allowance for ~/.local/bin/but, then reinstall
  4. If unrecoverable, manually download the tarball and place the binary in an exec-mounted PATH directory
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the install destination can execute binaries before installing
use std::os::unix::fs::PermissionsExt;
let bin_dir = home.join(".local/bin");
std::fs::create_dir_all(&bin_dir)?;
let probe = bin_dir.join(".exec-probe");
std::fs::write(&probe, "#!/bin/sh\nexit 0\n")?;
std::fs::set_permissions(&probe, std::fs::Permissions::from_mode(0o755))?;
let exec_ok = std::process::Command::new(&probe).status().is_ok_and(|s| s.success());
std::fs::remove_file(&probe)?;
anyhow::ensure!(exec_ok, "install dir is not executable (noexec mount?)");

Try / catch

if let Err(e) = but_installer::run_installation_with_version(request, false) {
    if e.to_string().contains("backup restoration also failed") {
        // both binaries fail: repair the environment (noexec mount, permissions, AV),
        // then reinstall from scratch
        repair_environment()?;
        return but_installer::run_installation_with_version(
            but_installer::VersionRequest::Release, false);
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Both old and new binaries fail to execute: ~/.local/bin mounted noexec, the system's glibc or dynamic loader broke independently (OS upgrade in progress), disk corruption, or endpoint security quarantining every newly written executable including the restore.

Common situations: noexec home partitions in hardened setups; distro upgrade replacing glibc mid-update; security software treating the rename-back as a new file and blocking it.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/52e1774513d8081f. Report an issue: GitHub.