gitbutlerapp/gitbutler · error

Final installation verification failed

Error message

Final installation verification failed

What it means

The Linux installer copies the new but binary into place (fs::copy because /tmp is often tmpfs on a different mount, so rename would fail cross-device), sets mode 0o755, then validates the result by executing it. This warn fires when validation fails; with a backup present it renames the backup over the new binary and re-validates, bailing either 'restored' or 'restoration also failed'.

Source

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

    info(&format!(
        "Installing{} to {}...",
        channel
            .map(|c| format!(" channel {}", c.display_name()))
            .unwrap_or_default(),
        install_bin_path.to_string_lossy(),
    ));

    // NOTE: Must copy rather than rename. Rename assumes source and dest are on the same mount
    // point, but /tmp on Linux systems is very often an in-memory file system (tmpfs) and thus on
    // 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!(

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Check disk space and re-run the installer — truncated copies are the most common cause
  2. Verify the downloaded binary's architecture matches uname -m
  3. Confirm the install path is not mounted noexec (mount | grep home)
  4. If the bail says the backup was restored, retry later; if 'but' is broken with no backup, reinstall from the official installer
Defensive patterns

Strategy: fallback

Validate before calling

// Validate the artifact before touching the installed path
fn binary_runs(path: &std::path::Path) -> bool {
    std::process::Command::new(path).arg("--version").output().is_ok()
}
// run on the staged or downloaded binary first

Try / catch

if !validate_installed_binary(&install_bin_path) {
    if let Some(but_backup) = but_backup {
        fs::rename(&but_backup, &install_bin_path)?;
        if !validate_installed_binary(&install_bin_path) {
            bail!("restore failed — 'but' may not work");
        }
        bail!("Installation failed but your previous installation was restored");
    }
    bail!("Installation failed and no backup available to restore");
}

Prevention

When it happens

Trigger: validate_installed_binary(&install_bin_path) returns false at crates/but-installer/src/install_linux.rs:109 — the copied binary is truncated or corrupt (disk filled during the copy), built for the wrong architecture or libc, or cannot execute (permissions, noexec mount).

Common situations: Disk filling mid-copy; an arm64 binary on x86_64 or vice versa; home directory mounted noexec; glibc older than the binary requires.

Related errors


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