{"record":{"id":"c02f63b065e5e91a","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-command-is-empty","errorCode":null,"errorMessage":"Codex command is empty","messagePattern":"Codex command is empty","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/launcher.rs","lineNumber":816,"sourceCode":"                command,\n                wait_strategy: ProcessWaitStrategy::ExternalWaitCommand,\n                macos_cleanup_policy: Some(cleanup_policy),\n            });\n        }\n\n        let command = if let Some(inspector_port) = native_menu_inspector_port {\n            build_codex_command_with_native_menu_inspector(\n                app_dir,\n                debug_port,\n                inspector_port,\n                &launch_extra_args,\n            )\n        } else {\n            build_codex_command(app_dir, debug_port, &launch_extra_args)\n        };\n        let executable = command\n            .first()\n            .ok_or_else(|| anyhow::anyhow!(\"Codex command is empty\"))?;\n        let mut child_command = Command::new(executable);\n        child_command\n            .args(&command[1..])\n            .stdout(Stdio::null())\n            .stderr(Stdio::null());\n        #[cfg(windows)]\n        child_command.creation_flags(crate::windows_integration::CREATE_NO_WINDOW);\n        let child = child_command\n            .spawn()\n            .with_context(|| format!(\"failed to launch Codex executable {executable}\"))?;\n        *self.child.lock().await = Some(child);\n        if let Some(inspector_port) = native_menu_inspector_port {\n            start_native_menu_localizer(inspector_port);\n        }\n        Ok(CodexLaunch::Process {\n            command,\n            wait_strategy: ProcessWaitStrategy::TrackedChild,\n            macos_cleanup_policy: None,","sourceCodeStart":798,"sourceCodeEnd":834,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/launcher.rs#L798-L834","documentation":"In the non-.app launch branch (Windows/Linux or direct executable), the launcher builds the Codex argv via build_codex_command / build_codex_command_with_native_menu_inspector and requires a first element (the executable path from app_paths::build_codex_executable) before spawning (crates/codex-plus-core/src/launcher.rs:814-816). Both builders unconditionally start with the executable string, so this error is a defensive invariant against a modified/empty builder, not an expected runtime outcome.","triggerScenarios":"Launching with an app_dir that is not a .app bundle while a customized build_codex_command* variant returns an empty Vec — e.g. fork code that builds args conditionally, or a stubbed builder in tests that returns Vec::new().","commonSituations":"Forks that rewrite command assembly; unit tests mocking command builders with empty returns; regressions after changing build_codex_executable to return an empty PathBuf that then formats to an empty-but-present string (note: that yields an empty executable string, not this error — this error needs a literally empty Vec).","solutions":["Restore the unconditional executable first element in any forked build_codex_command* so the vec is never empty","Validate app_dir resolution before launching: ensure resolve_app_dir returned a real directory containing the platform binary","Treat as an assertion: log the full launch inputs and report upstream if it fires in an unmodified build"],"exampleFix":"// before (test stub that trips the guard)\nlet command: Vec<String> = Vec::new();\nlet executable = command.first().ok_or_else(|| anyhow!(\"Codex command is empty\"))?;\n\n// after\nlet mut command = vec![crate::app_paths::build_codex_executable(app_dir).to_string_lossy().to_string()];\ncommand.extend(build_codex_arguments(debug_port, extra_args));\nlet executable = &command[0];","handlingStrategy":"validation","validationCode":"let command = build_codex_command(app_dir, debug_port, &launch_extra_args);\nif command.first().map(String::is_empty).unwrap_or(true) {\n    anyhow::bail!(\"refusing to spawn: codex command empty for {}\", app_dir.display());\n}","typeGuard":"fn spawnable_command(cmd: &[String]) -> Option<&str> {\n    cmd.first().map(|s| s.as_str()).filter(|s| !s.trim().is_empty())\n}","tryCatchPattern":"// Invariant failure: report inputs, do not retry\nmatch launcher.launch().await {\n    Err(e) if e.to_string() == \"Codex command is empty\" => {\n        tracing::error!(?app_dir, %debug_port, \"command builder regression\");\n        return Err(e.context(\"command builder returned empty argv\"));\n    }\n    rest => rest,\n}","preventionTips":["Keep the executable as the unconditional first vec element in builder forks","Assert builder invariants in unit tests: first element equals build_codex_executable(app_dir)","Resolve and validate app_dir (directory containing the platform binary) before launching"],"tags":["rust","launcher","command-construction","defensive-check","cross-platform"],"backgroundTag":"empty-command-invocation","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}