gitbutlerapp/gitbutler · error · anyhow::Error

No CLI wrapper configured for {}

Error message

No CLI wrapper configured for {}

What it means

Thrown by MacosApplication::resolve_cli_wrapper_abspath in but-api's program-open flow when a configured macOS application declares no cli_wrapper_path. The routine needs the wrapper's location inside the .app bundle to build an absolute path next to the bundle directory; with the field None there is nothing to join, so it bails immediately. The application itself is located afterwards via Launch Services, so this error is purely about incomplete app registration data, not about the app being missing.

Source

Thrown at crates/but-api/src/open/program.rs:593

}

/// A canonically installed macOS application with a bundle ID and an optional CLI wrapper.
#[cfg(target_os = "macos")]
#[derive(Clone, Debug, PartialEq)]
pub struct MacosApplication {
    /// macOS bundle identifier for the application.
    pub bundle_identifier: String,
    /// Location of the CLI wrapper inside the application bundle, if it exists.
    pub cli_wrapper_path: Option<String>,
}

#[cfg(target_os = "macos")]
impl MacosApplication {
    #[cfg(target_os = "macos")]
    fn resolve_cli_wrapper_abspath(&self) -> anyhow::Result<PathBuf> {
        let app_dir_path = self.find_app_directory()?;
        let cli_wrapper_path = self.cli_wrapper_path.as_deref().ok_or_else(|| {
            anyhow::anyhow!("No CLI wrapper configured for {}", self.bundle_identifier)
        })?;
        Ok(app_dir_path.join(cli_wrapper_path))
    }

    #[cfg(target_os = "macos")]
    fn find_app_directory(&self) -> anyhow::Result<PathBuf> {
        use objc2_app_kit::NSWorkspace;
        use objc2_foundation::NSString;

        let workspace = NSWorkspace::sharedWorkspace();
        let bundle_identifier = NSString::from_str(&self.bundle_identifier);
        let app_url = workspace
            .URLForApplicationWithBundleIdentifier(&bundle_identifier)
            .ok_or_else(|| {
                anyhow::anyhow!(
                    "Could not find application for '{}'",
                    self.bundle_identifier
                )

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Set cli_wrapper_path on the MacosApplication entry to the wrapper's path inside the .app bundle (e.g. "Contents/MacOS/myapp-cli")
  2. Verify the wrapper actually ships in the installed bundle: ls /Applications/MyApp.app/Contents/MacOS/
  3. If the target app has no CLI, route the request away from resolve_cli_wrapper_abspath instead of leaving the field empty

Example fix

// before
MacosApplication {
    bundle_identifier: "com.example.MyApp".into(),
    cli_wrapper_path: None,
}

// after
MacosApplication {
    bundle_identifier: "com.example.MyApp".into(),
    cli_wrapper_path: Some("Contents/MacOS/myapp-cli".into()),
}
Defensive patterns

Strategy: validation

Validate before calling

// Rust: check before resolving the CLI wrapper path
match app.cli_wrapper_path.as_deref() {
    Some(p) if !p.is_empty() => {
        // safe to call resolve_cli_wrapper_abspath / the CLI-open flow
    }
    _ => {
        // pick another open strategy; do not enter the wrapper flow
    }
}

Type guard

fn has_cli_wrapper(app: &MacosApplication) -> bool {
    app.cli_wrapper_path.as_deref().is_some_and(|p| !p.is_empty())
}

Try / catch

match resolve(&app) {
    Err(err) if err.to_string().starts_with("No CLI wrapper configured") => {
        // fall back to a non-CLI open strategy
    }
    result => result,
}

Prevention

When it happens

Trigger: Calling the open-in-application flow (open_in_macos_application, which calls resolve_cli_wrapper_abspath) with a MacosApplication entry whose cli_wrapper_path is None - for example a newly registered app in the program table that never declared its wrapper, or a config/schema change that made the field optional and silently dropped old values.

Common situations: Adding a new macOS target app to the launcher configuration and forgetting cli_wrapper_path; a serialization change that loses the field for existing entries; a bundling change where the CLI wrapper was removed from the app bundle entirely.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/1674c090d8807a60. Report an issue: GitHub.