{"record":{"id":"3944b3810ae61066","repo":"gitbutlerapp/gitbutler","slug":"found-existing-but-symlink-pointing-to-existin","errorCode":null,"errorMessage":"Found existing 'but' symlink pointing to: {existing_target_str}","messagePattern":"Found existing 'but' symlink pointing to: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":153,"sourceCode":"        info(&format!(\n            \"Your original 'but' has been saved to: {}\",\n            but_backup.display()\n        ));\n    } else if but_symlink.is_symlink() {\n        let existing_target = fs::read_link(&but_symlink)?;\n        let existing_target_str = existing_target.to_string_lossy();\n\n        // Detect channel switching\n        let previous_channel = if existing_target_str.contains(\"Nightly\") {\n            Some(Channel::Nightly)\n        } else if existing_target_str.contains(\"/GitButler.app/\") {\n            Some(Channel::Release)\n        } else {\n            None\n        };\n\n        if previous_channel.is_none() {\n            warn(&format!(\n                \"Found existing 'but' symlink pointing to: {existing_target_str}\"\n            ));\n            warn(\"This will be replaced with GitButler's 'but' command\");\n            info(\n                \"Note: Your custom symlink setup will be overwritten. The original target is not a GitButler installation.\",\n            );\n        }\n    }\n\n    // Create temporary symlink to test the new installation\n    let new_app_macos_dir = install_app_new.join(\"Contents/MacOS/gitbutler-tauri\");\n    let _ = fs::remove_file(&but_new);\n\n    unix_fs::symlink(&new_app_macos_dir, &but_new)?;\n\n    // Verify the new installation works\n    let verify_status = Command::new(&but_new)\n        .arg(\"--version\")","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/install_macos.rs#L135-L171","documentation":"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.","triggerScenarios":"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').","commonSituations":"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.","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."],"exampleFix":"// before: unaware the custom symlink is clobbered\n$ brew install gitbutler\n// after: check and preserve the old target first\n$ readlink /usr/local/bin/but   # note target\n$ mv /usr/local/bin/but /usr/local/bin/my-but\n$ # then run the GitButler installer","handlingStrategy":"validation","validationCode":"const but = '/usr/local/bin/but';\nif (fs.existsSync(but) && fs.lstatSync(but).isSymbolicLink()) {\n  const target = fs.readlinkSync(but);\n  if (!target.includes('gitbutler')) {\n    console.warn(`Existing non-GitButler 'but' symlink -> ${target}; back it up before installing.`);\n  }\n}","typeGuard":"function isGitButlerSymlink(linkPath: string): boolean {\n  try { return fs.readlinkSync(linkPath).includes('gitbutler'); } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["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`."],"tags":["symlink","installer","macos","cli-conflict"],"backgroundTag":"file-already-exists","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}