gitbutlerapp/gitbutler · error
Failed to create symlink: {e} - attempting to restore backup
Error message
Failed to create symlink: {e} - attempting to restore backup What it means
Warning emitted in the final swap phase when unix_fs::symlink(final_target, ~/.local/bin/but) fails after the new app bundle was already moved into place (install_macos.rs:230-237). The installer then rolls back: removes the new bundle, renames GitButler.app.backup back over ~/Applications/GitButler.app, re-creates the old symlink, and bails with 'Failed to create symlink: {e}. Previous installation was restored.' Note the preceding let _ = fs::remove_file(&but_symlink) discards removal errors, so a leftover directory at that path surfaces here as EEXIST/IsADirectory rather than as a clean removal failure.
Source
Thrown at crates/but-installer/src/install_macos.rs:235
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!(
"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 {View on GitHub (pinned to caf1f223d3)
Solutions
- Inspect the blocking path: ls -ld ~/.local/bin/but - if it is a directory, remove it (rm -rf ~/.local/bin/but) and re-run the installer
- Fix ownership and writability: chown -R $(whoami) ~/.local/bin && chmod u+w ~/.local/bin
- Check disk and mount state: df -h ~ (the rollback renames need space and a writable volume)
- Confirm the rollback left a working CLI: but --version; the bail message states the previous installation was restored, so nothing is lost
- Re-run the installer once the path is clear
Example fix
# before ls -ld ~/.local/bin/but drwxr-xr-x 2 me staff 64 but # directory blocks symlink creation -> EEXIST # after rm -rf ~/.local/bin/but # re-run installer; swap + symlink succeed, no rollback needed
Defensive patterns
Strategy: validation
Validate before calling
# Before installing, make the swap target unobstructed: [ -d ~/.local/bin/but ] && rm -rf ~/.local/bin/but # a directory is the classic blocker [ -e ~/.local/bin/but ] && [ ! -L ~/.local/bin/but ] && mv ~/.local/bin/but /tmp/but.saved [ -w ~/.local/bin ] && [ -x ~/.local/bin ] || chmod u+rwx ~/.local/bin df -h ~ | tail -1 # renames need space on the same volume
Prevention
- Never create a directory named exactly 'but' in ~/.local/bin
- Keep ~/.local/bin user-owned and writable; check 'ls -ld ~/.local/bin' after any sudo-assisted setup
- If the failure fires anyway, trust but verify the rollback: run 'but --version' and only reinstall after clearing the blocking path
When it happens
Trigger: ~/.local/bin/but is a directory (fs::remove_file cannot remove directories, so symlink creation fails with 'File exists'/'Is a directory'); permission denied on ~/.local/bin; read-only filesystem or full disk; another process recreating the path mid-install.
Common situations: A directory named 'but' created by other tooling in ~/.local/bin; ~/.local/bin owned by root after sudo-assisted setup; sandboxed installers without write access; endpoint-security agents locking the path.
Related errors
- Failed to move new installation into place - restoring backu
- Moving it to {} to preserve your existing file
- Failed to create symlink: {e}. No backup available to restor
- A 'but' binary already exists at {} (not a symlink)
- Found existing 'but' symlink pointing to: {existing_target_s
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/31bcae047970ad0f.
Report an issue: GitHub.