gitbutlerapp/gitbutler · critical
Failed to create symlink and failed to remove new…
Error message
Failed to create symlink and failed to remove new installation during rollback: {}. Backup at: {} What it means
After the new .app was moved into place, creating the `but` symlink failed. Rollback tried to remove the newly installed app directory (fs::remove_dir_all(&install_app)) so the backup could be renamed back, and that removal itself failed. The system is left in a dual-state: new (possibly symlink-less) install plus a backup, so the error message reports the backup path.
Solutions
- Quit any GitButler processes (`pkill -f gitbutler-tauri`) and retry the install
- Manually complete the rollback: `rm -rf <new GitButler.app>` then `mv <backup path> <install path>` using the backup path in the message
- Check ~/.local/bin/but is not a directory (`file ~/.local/bin/but`); remove it so a symlink can replace it
- Investigate why remove_dir_all failed (immutable flags, permissions, AV lock) with `ls -laO` on the app bundle
Example fix
// before: manual recovery guesswork // after: scripted recovery from the message rm -rf ~/Applications/GitButler.app mv ~/.local/share/gitbutler/GitButler.app.backup ~/Applications/GitButler.app
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-checks: no processes using the app, symlink path clear
let but_path = home.join(".local/bin/but");
if let Ok(m) = fs::symlink_metadata(&but_path) {
if !m.file_type().is_symlink() {
bail!("{} is not a symlink - symlink creation will fail", but_path.display());
}
}
let _ = Command::new("pgrep").args(["-f", "gitbutler-tauri"]).output()?; Try / catch
if let Err(e) = installer::download_and_install_app(...) {
if e.to_string().contains("failed to remove new installation during rollback") {
// dual state: finish rollback manually using the backup path in the message
eprintln!("manually restore from the backup path shown in: {e}");
}
} Prevention
- Never launch the app during an in-progress install
- Keep ~/.local/bin/but as a symlink managed only by the installer
- Disable AV real-time scanning of the install directory during updates
- If you see this error, finish the rollback manually before retrying to avoid a divergent state
When it happens
Trigger: install_app when unix_fs::symlink(final_target, but_symlink) fails AND install_app_backup exists AND fs::remove_dir_all(&install_app) fails during rollback - typically because files in the new app bundle are in use or permission-protected.
Common situations: A GitButler process was launched from the new app between the rename and the symlink step; permission-controlled directories inside the new bundle (added malware protections, AV scan); the `but` path in ~/.local/bin is itself a directory so symlink(2) fails with EEXIST while rm -rf of the app also hits a busy file.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Failed to create symlink
- Failed to create symlink
- BUG: we do not create or work with symlinks
- Failed to create symlink
- Failed to install new version
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/36dc286f24f5bb57.
Report an issue: GitHub.
Appendix: 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 58e5313667)