gitbutlerapp/gitbutler · info

This will be replaced with GitButler's 'but' command

Error message

This will be replaced with GitButler's 'but' command

What it means

Companion warning emitted by install_app in the same previous_channel.is_none() branch of install_macos.rs. It tells the user that GitButler will replace the existing non-GitButler 'but' symlink with its own command. It is purely informational and always follows the 'Found existing ... symlink' warning.

Solutions

  1. Allow the replacement if the symlink is stale or no longer needed.
  2. Back up the current symlink (`ln -s $(readlink /usr/local/bin/but) ~/but-backup`) if you still need the old target.
  3. Choose a custom install location to avoid clobbering the existing symlink.
Defensive patterns

Strategy: validation

Validate before calling

// Same pre-check as 910; accept replacement only if target is stale/unneeded.
const target = fs.existsSync('/usr/local/bin/but') ? fs.readlinkSync('/usr/local/bin/but') : null;
console.log(target ? `installer will replace -> ${target}` : 'no existing symlink');

Prevention

When it happens

Trigger: Same as error 910: running download_and_install_app -> install_app on macOS when a 'but' symlink exists whose target is not a GitButler channel installation.

Common situations: First-time GitButler installs on machines that already used the name 'but' for another CLI; users upgrading from a manual install who no longer remember what the symlink pointed to.

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


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/e5cce79e35e09cd7. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-installer/src/install_macos.rs:156

        ));
    } 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")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status();

View on GitHub (pinned to 58e5313667)