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
- Set cli_wrapper_path on the MacosApplication entry to the wrapper's path inside the .app bundle (e.g. "Contents/MacOS/myapp-cli")
- Verify the wrapper actually ships in the installed bundle: ls /Applications/MyApp.app/Contents/MacOS/
- 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
- Treat cli_wrapper_path as required when registering apps that support CLI open
- Add a startup validation pass that rejects MacosApplication entries with a None wrapper
- Cover the open configuration with a test asserting every macOS entry has cli_wrapper_path set
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
- {terminal_name} exited with non-zero status: {status_code}
- Failed to open {terminal_name} ({status_code}): {stderr}
- Unknown terminal: {terminal_id}
- failed to open {paths:?} with app bundle identifier '{}'
- id or name must be specified
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/1674c090d8807a60.
Report an issue: GitHub.