{"record":{"id":"9b049e44a41c887c","repo":"gitbutlerapp/gitbutler","slug":"cliinstallcancelled","errorCode":"CliInstallCancelled","errorMessage":"CLI install cancelled","messagePattern":"CLI install cancelled","errorType":"error_code","errorClass":"ErrorContext","httpStatus":null,"severity":"info","filePath":"crates/but-action/src/cli.rs","lineNumber":85,"sourceCode":"                    ln -sf \\'{}\\' \\'{UNIX_LINK_PATH}\\' \\\n                \\\" with administrator privileges\",\n                    cli_path.display()\n                ),\n            ])\n            .stdout(std::process::Stdio::inherit())\n            .stderr(std::process::Stdio::inherit())\n            .status()\n            .context(\"Failed to run osascript\")?;\n\n        if status.success() {\n            Ok(())\n        } else if status.code() == Some(1) {\n            // osascript exits 1 when the user dismisses the admin-privileges\n            // prompt. This is a benign abort, not an error — tag it with a\n            // dedicated Code so the frontend can react based on the code\n            // rather than matching on an English message.\n            Err(\n                anyhow!(\"osascript exited with status 1\").context(ErrorContext::new_static(\n                    Code::CliInstallCancelled,\n                    \"CLI install cancelled\",\n                )),\n            )\n        } else {\n            Err(anyhow!(\n                \"osascript exited with status {}\",\n                status\n                    .code()\n                    .map(|c| c.to_string())\n                    .unwrap_or_else(|| \"unknown\".into())\n            ))\n        }\n    } else {\n        Err(anyhow!(\n            \"Would probably need to run \\\"ln -sf '{}' '{UNIX_LINK_PATH}'\\\"{privilege}\",\n            cli_path.display(),\n            privilege = if can_elevate_privileges {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-action/src/cli.rs#L67-L103","documentation":"Constructed in crates/but-action/src/cli.rs:85 when the macOS osascript-based CLI installer (copying the but binary into place with admin privileges) exits with status 1 — the user dismissed the admin-privileges prompt. It is intentionally benign and tagged with the but_error Code::CliInstallCancelled so frontends classify by code (via the anyhow error chain) instead of matching the English message; the desktop app shows a neutral info toast rather than an error.","triggerScenarios":"Running the CLI install action on macOS and clicking Cancel on the admin privileges dialog; the osascript prompt interrupted or dismissed before credentials are entered.","commonSituations":"Users aborting the sudo/admin prompt; prompt fatigue when the dialog appears twice; managed Macs where the privileges dialog is blocked by device policy.","solutions":["Re-run the CLI install action and complete the admin prompt to finish the install","Or install manually: copy the but binary into the target bin directory with sudo","Frontends: match Code::CliInstallCancelled via the error chain and show retry/manual instructions, not an error","If the prompt never appears, check that /usr/bin/osascript is permitted by management profiles"],"exampleFix":"// before\nErr(anyhow!(\"osascript exited with status 1\").context(ErrorContext::new_static(\n\tCode::CliInstallCancelled,\n\t\"CLI install cancelled\",\n)))\n\n// after — a caller classifying by code instead of the message\nuse but_error::{AnyhowContextExt, Code};\n\nif let Some(ctx) = err.custom_context() {\n\tif ctx.code == Code::CliInstallCancelled {\n\t\treturn Outcome::Cancelled; // benign: show info toast, offer retry\n\t}\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"use but_error::{AnyhowContextExt, Code};\n\nmatch install_cli() {\n    Err(e) if e.custom_context().is_some_and(|c| c.code == Code::CliInstallCancelled) => {\n        // benign: the user dismissed the macOS admin prompt — offer retry or manual install\n        show_info_toast(\"CLI install cancelled\", [\"Retry\", \"Show manual steps\"]);\n    }\n    res => res?,\n}","preventionTips":["Classify by but_error Code via the error chain, never by matching the message string","Treat CliInstallCancelled as info with a retry affordance, not an error","Always offer a documented manual install path as the fallback","Check device-policy permissions for osascript on managed Macs when installs silently cancel"],"tags":["macos","cli-install","user-cancellation","rust"],"backgroundTag":"user-cancelled-operation","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}