{"record":{"id":"3ac7058d1d7ba5a7","repo":"gitbutlerapp/gitbutler","slug":"a-but-binary-already-exists-at-not-a-symlink","errorCode":null,"errorMessage":"A 'but' binary already exists at {} (not a symlink)","messagePattern":"A 'but' binary already exists at (.+?) \\(not a symlink\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":126,"sourceCode":"    }\n\n    // Create bin directory\n    let bin_dir = home_dir.join(\".local/bin\");\n    fs::create_dir_all(&bin_dir)?;\n\n    // Check for existing 'but' and detect channel switching\n    let but_symlink = bin_dir.join(\"but\");\n    let but_new = bin_dir.join(\"but.new\");\n\n    if but_symlink.exists() && !but_symlink.is_symlink() {\n        // 'but' exists but is not a symlink\n        let now = std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .unwrap_or_default()\n            .as_secs();\n        let timestamp = format!(\"{now}\");\n        let but_backup = bin_dir.join(format!(\"but.backup.{timestamp}\"));\n        warn(&format!(\n            \"A 'but' binary already exists at {} (not a symlink)\",\n            but_symlink.display()\n        ));\n        warn(&format!(\n            \"Moving it to {} to preserve your existing file\",\n            but_backup.display()\n        ));\n        fs::rename(&but_symlink, &but_backup)?;\n        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\") {","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-installer/src/install_macos.rs#L108-L144","documentation":"The macOS installer manages ~/.local/bin/but as a symlink to the app's bundled CLI. When a regular file already sits at that path (a user's own script or a manually copied binary), the installer warns, renames the existing file to but.backup.<unix-timestamp> to preserve it, and continues creating its symlink. Nothing of the user's is deleted, but their `but` command now resolves to GitButler's CLI.","triggerScenarios":"~/.local/bin/but exists and is not a symlink when the installer runs (crates/but-installer/src/install_macos.rs:126) — the user previously copied a binary there, another tool wrote a real-file shim, or an old manual install left a file behind.","commonSituations":"Custom `but` scripts from other tooling; PATH managers writing real-file shims; users who followed older manual CLI install docs.","solutions":["If you had a custom binary or script, recover it from ~/.local/bin/but.backup.<timestamp> and rename it back or elsewhere on PATH","To adopt the official CLI wholesale, remove leftover but.backup.* files","Verify afterwards: ls -l ~/.local/bin/but should show a symlink into the app bundle","Expect the warning again on future updates only if a new non-symlink file reappears at the path"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let but_symlink = bin_dir.join(\"but\");\nif but_symlink.exists() && !but_symlink.is_symlink() {\n    // decide before installing: back it up yourself, or let the installer rename it\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Install the CLI via the app so it owns the ~/.local/bin/but symlink","Never park custom binaries at that exact path","After updates, check but.backup.* files to recover anything you owned"],"tags":["rust","macos","installer","symlink","path-conflict","cli"],"backgroundTag":"conflicting-binary-path","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}