{"record":{"id":"314fb4c46b6b257a","repo":"gitbutlerapp/gitbutler","slug":"unexpected-cli-installer-response","errorCode":null,"errorMessage":"Unexpected CLI installer response","messagePattern":"Unexpected CLI installer response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-action/src/cli.rs","lineNumber":239,"sourceCode":"\"#;\n\n#[cfg(any(target_os = \"macos\", all(test, unix)))]\nfn check_cli_install_output(output: std::process::Output) -> anyhow::Result<()> {\n    anyhow::ensure!(\n        output.status.success(),\n        \"CLI installation failed ({}): {}\",\n        output.status,\n        String::from_utf8_lossy(&output.stderr).trim()\n    );\n    if output.stdout == b\"gitbutler-cli-install-cancelled\\n\" {\n        return Err(\n            anyhow!(\"Administrator authorization was cancelled\").context(ErrorContext::new_static(\n                Code::CliInstallCancelled,\n                \"CLI install cancelled\",\n            )),\n        );\n    }\n    anyhow::ensure!(\n        output.stdout == b\"gitbutler-cli-installed\\n\",\n        \"Unexpected CLI installer response\"\n    );\n    Ok(())\n}\n\n#[cfg(target_os = \"macos\")]\npub fn auto_fix_broken_but_cli_symlink() {\n    let Ok(absolute_link_destination) = std::fs::read_link(UNIX_LINK_PATH) else {\n        return;\n    };\n    if absolute_link_destination.exists() {\n        return;\n    }\n\n    let result = get_cli_path().and_then(|source| {\n        do_install_cli_v2(\n            &source,","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-action/src/cli.rs#L221-L257","documentation":"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.","triggerScenarios":"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.","commonSituations":"Mismatched app/CLI versions where the bundled AppleScript was changed; output piping/logging wrappers injecting text into stdout; tampered or corrupted app resources.","solutions":["Reinstall the GitButler app so the bundled install script matches the code that validates it (version alignment).","Run the install without output-wrapping shells/aliases that could add text to stdout.","If developing, confirm the script's success path still returns exactly 'gitbutler-cli-installed' and the cancellation path 'gitbutler-cli-install-cancelled'."],"exampleFix":"// before: script edited to return 'ok'\nreturn \"ok\" -- -> ensure! fails: Unexpected CLI installer response\n\n// after\nreturn \"gitbutler-cli-installed\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = install_cli_link_escalated(...) {\n    if format!(\"{e:#}\").contains(\"Unexpected CLI installer response\") {\n        // app/script version mismatch: reinstall the app so the bundled script matches\n    }\n}","preventionTips":["Keep the app and its bundled install script version-aligned; never hand-edit the AppleScript.","Run installs without stdout-redirecting wrappers or shell profile noise.","Preserve the exact success/cancellation sentinels when modifying the installer."],"tags":["macos","cli-installation","contract-violation","internal-invariant"],"backgroundTag":"unexpected-response-shape","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"}