gitbutlerapp/gitbutler · error
Failed to create symlink
Error message
Failed to create symlink: {e}. Previous installation was restored. What it means
Creating the `but` symlink failed after the new app was installed; the installer successfully rolled back by removing the new install, renaming the backup into place, and re-pointing the symlink at the restored app. The bail reports the original symlink error and confirms the previous installation is back.
Solutions
- Check what occupies ~/.local/bin/but (`file ~/.local/bin/but`) and remove/rename the non-symlink entry, then retry
- Fix permissions on ~/.local/bin (`sudo chown -R $(whoami) ~/.local/bin`)
- If Homebrew provides `but`, uninstall that formula first (`brew uninstall but`)
- Retry the install - the previous installation was restored, so it is safe
Example fix
// before
let _ = fs::remove_file(&but_symlink);
let symlink_result = unix_fs::symlink(&final_target, &but_symlink);
// after (caller-side precheck)
let meta = fs::symlink_metadata(bin_dir.join("but"));
if let Ok(m) = meta {
if !m.file_type().is_symlink() {
fs::rename(bin_dir.join("but"), bin_dir.join("but.user-override")).unwrap();
}
} Defensive patterns
Strategy: validation
Validate before calling
let link = home.join(".local/bin/but");
match fs::symlink_metadata(&link) {
Ok(m) if !m.file_type().is_symlink() => {
bail!("{} exists as a regular file/dir - move or remove it before install", link.display());
}
Err(e) if e.kind() != std::io::ErrorKind::NotFound => {
bail!("cannot inspect {}: {e}", link.display());
}
_ => {}
} Type guard
fn but_symlink_replaceable(path: &Path) -> bool {
fs::symlink_metadata(path)
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
} Try / catch
match result {
Err(e) if e.to_string().contains("Previous installation was restored") => {
eprintln!("fix ~/.local/bin/but and retry; old install is intact: {e}");
}
other => other?,
} Prevention
- Do not install `but` via Homebrew or copy a binary to ~/.local/bin/but manually
- Keep ~/.local/bin user-owned
- Let the installer own the symlink; remove foreign `but` entries first
- Re-run the installer after fixing PATH-managed conflicts
When it happens
Trigger: install_app when unix_fs::symlink(&final_target, &but_symlink) fails while a backup exists - e.g. ~/.local/bin/but exists as a regular file or directory (EEXIST/ENOTDIR), or the bin dir is read-only (EPERM).
Common situations: User previously managed `but` with Homebrew or a manually placed binary at ~/.local/bin/but; PATH management tools recreated the file; ~/.local/bin owned by root after an earlier sudo install.
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 and failed to remove new…
- Failed to create symlink
- Failed to create symlink
- Failed to install new version
- BUG: we do not create or work with symlinks
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/bd6735b69f5eabac.
Report an issue: GitHub.
Appendix: 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 58e5313667)