BigPizzaV3/CodexPlusPlus · error · anyhow::Error

macOS open command is empty

Error message

macOS open command is empty

What it means

On macOS, when the resolved app_dir ends in .app the launcher builds an `open`-style argv via build_macos_open_command (or its native-menu-inspector variant) and guards that the vector is non-empty before Command::new (crates/codex-plus-core/src/launcher.rs:784-786). The builders always seed the vector with the open executable path, so an empty command indicates a broken builder invocation (custom build_macos_open_command_* override, bad refactor, or empty app_dir producing degenerate output) rather than a normal runtime condition.

Source

Thrown at crates/codex-plus-core/src/launcher.rs:786

        if app_dir.extension().and_then(|value| value.to_str()) == Some("app") {
            let cleanup_policy = if is_macos_app_running(app_dir).await {
                MacosCleanupPolicy::SkipQuitBecauseAlreadyRunning
            } else {
                MacosCleanupPolicy::QuitIfNotPreviouslyRunning
            };
            let command = if let Some(inspector_port) = native_menu_inspector_port {
                build_macos_open_command_with_native_menu_inspector(
                    app_dir,
                    debug_port,
                    inspector_port,
                    &launch_extra_args,
                )
            } else {
                build_macos_open_command(app_dir, debug_port, &launch_extra_args)
            };
            let executable = command
                .first()
                .ok_or_else(|| anyhow::anyhow!("macOS open command is empty"))?;
            let child = Command::new(executable)
                .args(&command[1..])
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .spawn()
                .context("failed to launch macOS Codex app")?;
            *self.child.lock().await = Some(child);
            if let Some(inspector_port) = native_menu_inspector_port {
                start_native_menu_localizer(inspector_port);
            }
            return Ok(CodexLaunch::Process {
                command,
                wait_strategy: ProcessWaitStrategy::ExternalWaitCommand,
                macos_cleanup_policy: Some(cleanup_policy),
            });
        }

        let command = if let Some(inspector_port) = native_menu_inspector_port {

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. If you forked or wrapped build_macos_open_command*, restore the unconditional first element (the `open` executable) so the vec is never empty
  2. Check the resolved app_dir is non-empty and has extension .app before launch; a malformed dir points at an upstream app-path resolution problem
  3. If unreachable in your build, treat as an assertion failure: report the full settings/app_dir used and file it upstream rather than catching it

Example fix

// before
fn build_macos_open_command(app_dir: &Path, debug_port: u16, extra: &[String]) -> Vec<String> {
    let mut command = Vec::new(); // may stay empty
    ...
}

// after
fn build_macos_open_command(app_dir: &Path, debug_port: u16, extra: &[String]) -> Vec<String> {
    let mut command = vec!["/usr/bin/open".to_string()]; // never empty
    ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Before launch, sanity-check the macOS branch inputs
#[cfg(target_os = "macos")]
fn macos_launch_ready(app_dir: &Path) -> bool {
    app_dir.extension().and_then(|e| e.to_str()) == Some("app")
        && app_dir.join("Contents/MacOS").is_dir()
}

Type guard

fn non_empty_command(cmd: &[String]) -> bool {
    cmd.first().is_some_and(|exe| !exe.trim().is_empty())
}

Try / catch

// Guard is an invariant; if it ever fires, capture diagnostics rather than retry:
Err(e) if e.to_string() == "macOS open command is empty" => {
    anyhow::bail!("internal invariant broken: builder returned empty argv for {}", app_dir.display())
}

Prevention

When it happens

Trigger: app_dir has extension "app", is_macos launch branch is taken, and build_macos_open_command / build_macos_open_command_with_native_menu_inspector returns a Vec with zero elements — e.g. after a refactor that conditionally skips pushing the executable, or a copied/modified builder in a fork.

Common situations: Fork modifications to the macOS command builders; passing an empty Path as app_dir so path formatting yields nothing; regression after changing build_macos_open_command's return construction; this is effectively an unreachable defensive invariant in stock builds.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/a5fa90b78c509b43. Report an issue: GitHub.