{"record":{"id":"b081810c4cd7c3a0","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-app-server-executable-hint-error","errorCode":null,"errorMessage":"无法启动 Codex app-server（{executable}）{hint}（{error}）","messagePattern":"无法启动 Codex app-server（(.+?)）(.+?)（(.+?)）","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/connect/app_server.rs","lineNumber":118,"sourceCode":"        };\n        validate_codex_executable(executable)?;\n        let mut command = Command::new(executable);\n        command\n            .arg(\"app-server\")\n            .current_dir(&config.work_dir)\n            .stdin(Stdio::piped())\n            .stdout(Stdio::piped())\n            .stderr(Stdio::piped())\n            .kill_on_drop(true);\n        let mut child = command.spawn().map_err(|error| {\n            let hint = match error.kind() {\n                std::io::ErrorKind::NotFound => {\n                    \"：找不到该文件；填 Codex CLI 可执行文件的完整路径，或确保 codex 在 PATH 里\"\n                }\n                std::io::ErrorKind::PermissionDenied => \"：没有执行权限\",\n                _ => \"\",\n            };\n            anyhow::anyhow!(\"无法启动 Codex app-server（{executable}）{hint}（{error}）\")\n        })?;\n        let stdin = child\n            .stdin\n            .take()\n            .context(\"无法连接 Codex app-server stdin\")?;\n        let stdout = child\n            .stdout\n            .take()\n            .context(\"无法连接 Codex app-server stdout\")?;\n        // 别把 stderr 读了就扔——app-server 起不来时，真正的原因只在这里。\n        // 留最近几行，初始化失败时附到错误信息上。\n        let stderr_tail = Arc::new(Mutex::new(VecDeque::<String>::new()));\n        if let Some(stderr) = child.stderr.take() {\n            let sink = Arc::clone(&stderr_tail);\n            tokio::spawn(async move {\n                let mut lines = BufReader::new(stderr).lines();\n                while let Ok(Some(line)) = lines.next_line().await {\n                    let line = line.trim().to_string();","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/connect/app_server.rs#L100-L136","documentation":"Thrown when spawning the Codex app-server child process fails. The library wraps std::process::Command spawn errors and appends a hint depending on the io::ErrorKind: NotFound means the executable path does not exist (or is not on PATH), PermissionDenied means the file is not executable. The underlying OS error is preserved in {error}.","triggerScenarios":"Calling start (via start_helper_waiting_for_busy_port) with a configured Codex CLI path that does not exist, is not on PATH, or lacks the execute bit; also any other spawn failure (e.g. resource limits, E2BIG).","commonSituations":"User left the codex executable path empty or mistyped after upgrading; codex installed via npm/cargo not on the GUI app's PATH; binary downloaded without +x; antivirus or sandbox blocking exec.","solutions":["Set the full absolute path to the Codex CLI executable in settings (e.g. /usr/local/bin/codex or ~/.cargo/bin/codex)","Verify the binary exists and is executable: ls -l $(which codex) / chmod +x","Ensure PATH includes the install dir when launching from a GUI app (GUI apps inherit launchd PATH, not shell PATH)","Reinstall or update the Codex CLI if the binary is missing after an upgrade"],"exampleFix":"// before (settings)\ncodexPath: \"codex\"\n// after\ncodexPath: \"/usr/local/bin/codex\"","handlingStrategy":"try-catch","validationCode":"let p = settings.codex_path.clone();\nif p.is_empty() || !std::path::Path::new(&p).exists() {\n    return Err(format!(\"Codex CLI not found at '{p}'; set the full executable path\"));\n}\n#[cfg(unix)]\n{\n    use std::os::unix::fs::PermissionsExt;\n    let mode = std::fs::metadata(&p)?.permissions().mode();\n    if mode & 0o111 == 0 { return Err(\"Codex CLI is not executable\".into()); }\n}","typeGuard":"fn is_executable_file(path: &str) -> bool {\n    let p = std::path::Path::new(path);\n    p.is_file() && std::fs::metadata(p)\n        .map(|m| !m.permissions().readonly())\n        .unwrap_or(false)\n}","tryCatchPattern":"match app_server::start(config).await {\n    Err(e) if e.to_string().contains(\"无法启动 Codex app-server\") => {\n        eprintln!(\"Check the codex executable path and permissions: {e}\");\n    }\n    Err(e) => return Err(e),\n    Ok(v) => v,\n}","preventionTips":["Always store an absolute path to the Codex CLI in settings","After upgrading or reinstalling codex, re-verify the configured path","On GUI apps remember PATH differs from your shell; use absolute paths","Keep the binary executable (chmod +x) and out of quarantined locations"],"tags":["process-spawn","rust","io"],"backgroundTag":"command-not-found","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}