gitbutlerapp/gitbutler · warning
A 'but' binary already exists at
Error message
A 'but' binary already exists at {} (not a symlink) What it means
While wiring up the 'but' CLI symlink in the bin directory, install_app on macOS finds an existing non-symlink file at the symlink target path. It warns that a real 'but' binary already exists and that it will be moved aside (to but.backup.<timestamp>) to preserve the user's file before creating the symlink.
Solutions
- Let the installer proceed: your existing binary is preserved as but.backup.<timestamp> in the same directory.
- If you want to keep your custom build authoritative, remove it from the installer's bin dir or point the installer at a different bin directory.
- Delete the stale binary yourself first if you no longer need it (rm <bin_dir>/but) and re-run the installer.
- Check whether a package manager owns the binary (e.g. dpkg -S / brew list) and uninstall it there to avoid the collision recurring.
Defensive patterns
Strategy: validation
Validate before calling
# Detect a non-symlink 'but' in your bin dir before installing
import os
def but_is_symlink(bin_dir):
p = os.path.join(bin_dir, 'but')
return os.path.islink(p) or not os.path.exists(p) Try / catch
// Move aside any real file before creating the symlink
if path.is_file() && !path.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false) {
let backup = bin_dir.join(format!("but.backup.{}", timestamp()));
fs::rename(&path, &backup)?;
} Prevention
- Use one installation method consistently (don't mix copied binaries and symlinked installs)
- Check which package manager owns an existing 'but' binary (which -a but) before installing
- Keep custom builds out of the installer's target bin directory
When it happens
Trigger: Raised in install_app (download_and_install_app) when the path bin_dir/but exists but is not a symlink — i.e. a previous install copied a real binary there, or the user placed a custom build named 'but' in their PATH directory.
Common situations: Upgrading from an install method that copied the binary instead of symlinking; a locally built 'but' binary sitting in ~/.local/bin or /usr/local/bin; package-manager-managed 'but' binary colliding with the installer's symlink.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Failed to create symlink
- Found existing 'but' symlink pointing to
- Moving it to to preserve your existing file
- This will be replaced with GitButler's 'but' command
- Could not remove quarantine attribute
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/3ac7058d1d7ba5a7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-installer/src/install_macos.rs:126
}
// Create bin directory
let bin_dir = home_dir.join(".local/bin");
fs::create_dir_all(&bin_dir)?;
// Check for existing 'but' and detect channel switching
let but_symlink = bin_dir.join("but");
let but_new = bin_dir.join("but.new");
if but_symlink.exists() && !but_symlink.is_symlink() {
// 'but' exists but is not a symlink
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let timestamp = format!("{now}");
let but_backup = bin_dir.join(format!("but.backup.{timestamp}"));
warn(&format!(
"A 'but' binary already exists at {} (not a symlink)",
but_symlink.display()
));
warn(&format!(
"Moving it to {} to preserve your existing file",
but_backup.display()
));
fs::rename(&but_symlink, &but_backup)?;
info(&format!(
"Your original 'but' has been saved to: {}",
but_backup.display()
));
} else if but_symlink.is_symlink() {
let existing_target = fs::read_link(&but_symlink)?;
let existing_target_str = existing_target.to_string_lossy();
// Detect channel switching
let previous_channel = if existing_target_str.contains("Nightly") {View on GitHub (pinned to 58e5313667)