gitbutlerapp/gitbutler · critical

Failed to create symlink and failed to remove new installati

Error message

Failed to create symlink and failed to remove new installation during rollback: {}. Backup at: {}

What it means

After the app swap, recreating the 'but' symlink failed; rollback then tried to remove the new app directory to put the backup back, and that removal also failed. The message reports both errors plus the backup path. Likely end state: new app installed but no working but symlink, and the previous app still sitting at the printed backup path.

Source

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

        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!(
                    "Failed to create symlink and failed to remove new installation during rollback: {}. Backup at: {}",
                    remove_err,
                    install_app_backup.display()
                );
            }
            fs::rename(&install_app_backup, &install_app)?;

            let restored_target = install_app.join("Contents/MacOS/gitbutler-tauri");
            let _ = fs::remove_file(&but_symlink);
            let _ = unix_fs::symlink(&restored_target, &but_symlink);

            bail!("Failed to create symlink: {e}. Previous installation was restored.");
        } else {
            bail!("Failed to create symlink: {e}. No backup available to restore.");
        }
    }

    if !validate_installed_binary(&but_symlink) {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Manually finish the rollback: rm -rf the new app dir, mv '<backup path from the message>' into the install location, then ln -s <app>/Contents/MacOS/gitbutler-tauri ~/.local/bin/but
  2. If the new app actually runs, keep it and just recreate the symlink by hand; delete the backup
  3. Fix permissions on both the symlink dir and the app dir, then rerun the installer
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the symlink directory is writable before install
let bin_dir = home.join(".local/bin");
std::fs::create_dir_all(&bin_dir)?;
let probe = bin_dir.join(".write-probe");
std::fs::write(&probe, b"")?;
std::fs::remove_file(&probe)?;

Try / catch

if let Err(e) = but_installer::run_installation_with_version(request, false) {
    let msg = e.to_string();
    if msg.contains("failed to remove new installation during rollback") {
        // manual recovery using the printed "Backup at:" path
        let backup = msg.split("Backup at: ").nth(1).unwrap_or_default().trim();
        manual_restore(std::path::Path::new(backup))?;
    }
    return Err(e);
}

Prevention

When it happens

Trigger: unix_fs::symlink fails (the but link path is occupied by a non-removable object or its directory is not writable) while simultaneously remove_dir_all on the new app dir fails (bundle files locked by a running app, SIP/MDM protection, permissions).

Common situations: App launched between swap and symlink; permissions changed on ~/.local/bin or the install dir since the last update; MDM policy blocking both operations.

Related errors


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