gitbutlerapp/gitbutler · error

Unexpected CLI installer response

Error message

Unexpected CLI installer response

What it means

After a successful exit status, check_cli_install_output requires the installer script's stdout to be exactly the sentinel 'gitbutler-cli-installed' (or the known cancellation sentinel). Any other stdout — even with exit code 0 — means the script's contract is broken, so this internal-invariant error is thrown to catch corrupted installs, wrapper interference, or script/API drift between app versions.

Solutions

  1. Reinstall the GitButler app so the bundled install script matches the code that validates it (version alignment).
  2. Run the install without output-wrapping shells/aliases that could add text to stdout.
  3. If developing, confirm the script's success path still returns exactly 'gitbutler-cli-installed' and the cancellation path 'gitbutler-cli-install-cancelled'.

Example fix

// before: script edited to return 'ok'
return "ok" -- -> ensure! fails: Unexpected CLI installer response

// after
return "gitbutler-cli-installed"
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = install_cli_link_escalated(...) {
    if format!("{e:#}").contains("Unexpected CLI installer response") {
        // app/script version mismatch: reinstall the app so the bundled script matches
    }
}

Prevention

When it happens

Trigger: install_cli_link_escalated's AppleScript exits 0 but stdout is neither 'gitbutler-cli-installed\n' nor 'gitbutler-cli-install-cancelled\n' — e.g. the script was modified, its output was prefixed by shell profile noise, or an older/newer script version prints a different sentinel.

Common situations: Mismatched app/CLI versions where the bundled AppleScript was changed; output piping/logging wrappers injecting text into stdout; tampered or corrupted app resources.

Related errors


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

Appendix: source

Thrown at crates/but-action/src/cli.rs:239

"#;

#[cfg(any(target_os = "macos", all(test, unix)))]
fn check_cli_install_output(output: std::process::Output) -> anyhow::Result<()> {
    anyhow::ensure!(
        output.status.success(),
        "CLI installation failed ({}): {}",
        output.status,
        String::from_utf8_lossy(&output.stderr).trim()
    );
    if output.stdout == b"gitbutler-cli-install-cancelled\n" {
        return Err(
            anyhow!("Administrator authorization was cancelled").context(ErrorContext::new_static(
                Code::CliInstallCancelled,
                "CLI install cancelled",
            )),
        );
    }
    anyhow::ensure!(
        output.stdout == b"gitbutler-cli-installed\n",
        "Unexpected CLI installer response"
    );
    Ok(())
}

#[cfg(target_os = "macos")]
pub fn auto_fix_broken_but_cli_symlink() {
    let Ok(absolute_link_destination) = std::fs::read_link(UNIX_LINK_PATH) else {
        return;
    };
    if absolute_link_destination.exists() {
        return;
    }

    let result = get_cli_path().and_then(|source| {
        do_install_cli_v2(
            &source,

View on GitHub (pinned to 58e5313667)