{"record":{"id":"680b930637d1f9cb","repo":"gitbutlerapp/gitbutler","slug":"osascript-exited-with-status","errorCode":null,"errorMessage":"osascript exited with status {}","messagePattern":"osascript exited with status (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-action/src/cli.rs","lineNumber":91,"sourceCode":"            .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 {\n                \" with root permissions\"\n            } else {\n                \"\"\n            }\n        ))\n    }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-action/src/cli.rs#L73-L109","documentation":"Thrown by GitButler's macOS CLI installer (do_install_cli) when the osascript child that runs `ln -sf <binary> /usr/local/bin/but` with administrator privileges exits with a status other than 0 (success) or 1 (user dismissed the password prompt, which is reported separately as Code::CliInstallCancelled). The message embeds the raw exit status, or 'unknown' when osascript was killed by a signal. It means the privileged shell script itself failed, not that the user merely cancelled.","triggerScenarios":"Calling do_install_cli(InstallMode::AllowPrivilegeElevation) on macOS after the unprivileged symlink attempt failed: the code runs /usr/bin/osascript -e 'do shell script \"ln -sf ... /usr/local/bin/but\" with administrator privileges' and the child exits with a code other than 0/1 — wrong admin password entered until osascript gives up, /usr/local/bin missing, `ln` failing (invalid binary path, busy target), or osascript terminated by a signal.","commonSituations":"Fresh macOS where /usr/local/bin does not exist (Homebrew lives in /opt/homebrew); user mistypes the admin password repeatedly; the app binary moved or was deleted between discovery and elevation; corporate MDM blocking osascript privilege escalation.","solutions":["Inspect the embedded exit status: status 1 never reaches this error (it becomes Code::CliInstallCancelled); other non-zero statuses usually mean authentication or `ln` failed — re-run the install and enter correct admin credentials.","Verify /usr/local/bin exists: `ls -ld /usr/local/bin`, and create it with `sudo mkdir -p /usr/local/bin` if missing.","Create the symlink manually with the exact command shown in the sibling hint error: sudo ln -sf '<cli_path>' /usr/local/bin/but.","Confirm the binary path returned by get_cli_path() still exists at elevation time and is not on an ephemeral/network volume.","If osascript elevation is blocked by MDM, fall back to a user-writable PATH directory such as ~/.local/bin."],"exampleFix":"# manual fallback (paths come from the sibling hint error)\nsudo mkdir -p /usr/local/bin\nsudo ln -sf '/Applications/GitButler.app/Contents/MacOS/tauri' /usr/local/bin/but\nbut --version","handlingStrategy":"retry","validationCode":"use std::path::Path;\n\nfn osascript_elevation_likely_to_succeed() -> bool {\n    let link_dir = Path::new(\"/usr/local/bin\");\n    link_dir.is_dir() // missing dir is the most common non-password failure\n}","typeGuard":null,"tryCatchPattern":"match do_install_cli(InstallMode::AllowPrivilegeElevation) {\n    Ok(()) => Ok(()),\n    Err(err) if matches!(err.downcast_ref::<but_error::ErrorContext>().map(|c| c.code), Some(but_error::Code::CliInstallCancelled)) => Ok(()), // user dismissed prompt: not an error\n    Err(err) => {\n        // real osascript failure: offer the manual `ln -sf` command, allow one retry\n        Err(err)\n    }\n}","preventionTips":["Ensure /usr/local/bin exists before triggering the installer.","Keep the app binary at a stable absolute path so the elevated ln target stays valid.","Treat exit status 1 as user cancellation (Code::CliInstallCancelled) and never surface it as a failure."],"tags":["macos","cli-install","osascript","administrator-privileges","symlink"],"backgroundTag":"privilege-elevation-failed","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}