gitbutlerapp/gitbutler · error

osascript exited with status {}

Error message

osascript exited with status {}

What it means

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.

Source

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

            .stderr(std::process::Stdio::inherit())
            .status()
            .context("Failed to run osascript")?;

        if status.success() {
            Ok(())
        } else if status.code() == Some(1) {
            // osascript exits 1 when the user dismisses the admin-privileges
            // prompt. This is a benign abort, not an error — tag it with a
            // dedicated Code so the frontend can react based on the code
            // rather than matching on an English message.
            Err(
                anyhow!("osascript exited with status 1").context(ErrorContext::new_static(
                    Code::CliInstallCancelled,
                    "CLI install cancelled",
                )),
            )
        } else {
            Err(anyhow!(
                "osascript exited with status {}",
                status
                    .code()
                    .map(|c| c.to_string())
                    .unwrap_or_else(|| "unknown".into())
            ))
        }
    } else {
        Err(anyhow!(
            "Would probably need to run \"ln -sf '{}' '{UNIX_LINK_PATH}'\"{privilege}",
            cli_path.display(),
            privilege = if can_elevate_privileges {
                " with root permissions"
            } else {
                ""
            }
        ))
    }

View on GitHub (pinned to 2497b8007a)

Solutions

  1. 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.
  2. Verify /usr/local/bin exists: `ls -ld /usr/local/bin`, and create it with `sudo mkdir -p /usr/local/bin` if missing.
  3. Create the symlink manually with the exact command shown in the sibling hint error: sudo ln -sf '<cli_path>' /usr/local/bin/but.
  4. Confirm the binary path returned by get_cli_path() still exists at elevation time and is not on an ephemeral/network volume.
  5. If osascript elevation is blocked by MDM, fall back to a user-writable PATH directory such as ~/.local/bin.

Example fix

# manual fallback (paths come from the sibling hint error)
sudo mkdir -p /usr/local/bin
sudo ln -sf '/Applications/GitButler.app/Contents/MacOS/tauri' /usr/local/bin/but
but --version
Defensive patterns

Strategy: retry

Validate before calling

use std::path::Path;

fn osascript_elevation_likely_to_succeed() -> bool {
    let link_dir = Path::new("/usr/local/bin");
    link_dir.is_dir() // missing dir is the most common non-password failure
}

Try / catch

match do_install_cli(InstallMode::AllowPrivilegeElevation) {
    Ok(()) => Ok(()),
    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
    Err(err) => {
        // real osascript failure: offer the manual `ln -sf` command, allow one retry
        Err(err)
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/680b930637d1f9cb. Report an issue: GitHub.