gitbutlerapp/gitbutler · warning
Found existing 'but' symlink pointing to
Error message
Found existing 'but' symlink pointing to: {existing_target_str} What it means
During macOS installation, install_app checks the target path of an existing 'but' symlink in a bin directory. If the symlink's target does not resolve to a GitButler installation (previous_channel is None), the installer emits this warning including the existing target string before overwriting it. It is a heads-up that a user-managed symlink is about to be replaced.
Solutions
- Inspect the reported target with `readlink $(which but)`; if it belongs to another tool, rename that tool's binary or adjust your PATH before installing.
- Accept the warning if you no longer need the custom 'but' command - the installer will overwrite the symlink to point at GitButler.
- Re-install/restore the other tool afterwards under a different name if you still need both commands.
Example fix
// before: unaware the custom symlink is clobbered $ brew install gitbutler // after: check and preserve the old target first $ readlink /usr/local/bin/but # note target $ mv /usr/local/bin/but /usr/local/bin/my-but $ # then run the GitButler installer
Defensive patterns
Strategy: validation
Validate before calling
const but = '/usr/local/bin/but';
if (fs.existsSync(but) && fs.lstatSync(but).isSymbolicLink()) {
const target = fs.readlinkSync(but);
if (!target.includes('gitbutler')) {
console.warn(`Existing non-GitButler 'but' symlink -> ${target}; back it up before installing.`);
}
} Type guard
function isGitButlerSymlink(linkPath: string): boolean {
try { return fs.readlinkSync(linkPath).includes('gitbutler'); } catch { return false; }
} Prevention
- Run `readlink $(which but)` before installing to learn what currently owns the name.
- Keep custom tools out of standard bin paths when their names may collide with installers (e.g. install to ~/bin with an unusual name).
- Automate symlink backup in provisioning scripts: `ln -s $(readlink /usr/local/bin/but) ~/but-backup`.
When it happens
Trigger: Running the GitButler macOS installer (via download_and_install_app -> install_app) when /usr/local/bin/but (or equivalent) already exists as a symlink whose target is not a GitButler binary (e.g. points to a homebrew formula or a custom tool also named 'but').
Common situations: Users who previously aliased 'but' to some other tool (e.g. branch-util scripts, homebrew packages) install GitButler and the installer detects the conflict; environments with pre-existing PATH shims named 'but'; machines shared between tools using the same command name.
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
- This will be replaced with GitButler's 'but' command
- A 'but' binary already exists at
- Failed to create symlink
- Moving it to to preserve your existing file
- Could not remove quarantine attribute
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/3944b3810ae61066.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-installer/src/install_macos.rs:153
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") {
Some(Channel::Nightly)
} else if existing_target_str.contains("/GitButler.app/") {
Some(Channel::Release)
} else {
None
};
if previous_channel.is_none() {
warn(&format!(
"Found existing 'but' symlink pointing to: {existing_target_str}"
));
warn("This will be replaced with GitButler's 'but' command");
info(
"Note: Your custom symlink setup will be overwritten. The original target is not a GitButler installation.",
);
}
}
// Create temporary symlink to test the new installation
let new_app_macos_dir = install_app_new.join("Contents/MacOS/gitbutler-tauri");
let _ = fs::remove_file(&but_new);
unix_fs::symlink(&new_app_macos_dir, &but_new)?;
// Verify the new installation works
let verify_status = Command::new(&but_new)
.arg("--version")View on GitHub (pinned to 58e5313667)