gitbutlerapp/gitbutler · error

Failed to create symlink: {e}. Previous installation was res

Error message

Failed to create symlink: {e}. Previous installation was restored.

What it means

Symlink creation for 'but' failed, but the backup app was restored and the previous symlink target recreated - the prior installation is functional again. The original symlink error is embedded. Note the code ignores the result of remove_file on the old link first, so a directory named 'but' on the link path surfaces here as an EEXIST/EISDIR symlink failure.

Source

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

        // 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) {
        // Try to restore backup
        warn("Final installation verification failed - attempting to restore backup");
        if install_app_backup.exists() {
            fs::remove_dir_all(&install_app)?;
            fs::rename(&install_app_backup, &install_app)?;

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

            if validate_installed_binary(&but_symlink) {
                success("Backup was restored successfully");

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Inspect the embedded {e}: EEXIST/EISDIR - remove whatever occupies the link path (rm -rf ~/.local/bin/but if it is a stray directory)
  2. Fix ownership of the bin dir: chown -R $(whoami) ~/.local/bin
  3. Re-run the installer; the previous version keeps working meanwhile

Example fix

# before: a directory blocks the symlink path
ls -ld ~/.local/bin/but   # drwxr-xr-x ...
rm -rf ~/.local/bin/but
# after
but-installer 0.18.7
Defensive patterns

Strategy: validation

Validate before calling

// The 'but' link path must be absent or a removable file, never a directory
let but_link = home.join(".local/bin/but");
if but_link.symlink_metadata().is_ok() {
    anyhow::ensure!(!but_link.is_dir(),
        "~/.local/bin/but is a directory; remove it before reinstalling");
}

Try / catch

match but_installer::run_installation_with_version(request, false) {
    Err(e) if e.to_string().contains("Failed to create symlink")
        && e.to_string().contains("was restored") =>
    {
        // old version intact; clear the link path and retry once
        let _ = std::fs::remove_file(home.join(".local/bin/but"));
        but_installer::run_installation_with_version(request, false)
    }
    result => result,
}

Prevention

When it happens

Trigger: A directory (not a file/symlink) occupies ~/.local/bin/but so the pre-remove fails silently and symlink(2) returns EEXIST; or the symlink directory is not writable by the current user. Backup exists, so restore succeeds.

Common situations: A stray 'but' directory from a botched manual install; root-owned ~/.local/bin after running something with sudo; restored cleanly afterwards.

Related errors


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