{"record":{"id":"c613051e68ed9f7d","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-app-server-exit-status-suffix","errorCode":null,"errorMessage":"Codex app-server 已关闭{exit_status_suffix}","messagePattern":"Codex app-server 已关闭(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/codex-plus-core/src/connect/app_server.rs","lineNumber":377,"sourceCode":"            if response_id(&message) != Some(id) {\n                continue;\n            }\n            if let Some(error) = rpc_error(&message) {\n                bail!(\"Codex app-server {method} 失败：{error}\");\n            }\n            return Ok(message.get(\"result\").cloned().unwrap_or(Value::Null));\n        }\n    }\n\n    async fn read_message(&mut self, timeout: Duration) -> anyhow::Result<Value> {\n        loop {\n            let line = tokio::time::timeout(timeout, self.stdout.next_line())\n                .await\n                .context(\"等待 Codex app-server 响应超时\")??;\n            let Some(line) = line else {\n                self.running = false;\n                let exit = self.child.try_wait().ok().flatten();\n                bail!(\n                    \"Codex app-server 已关闭{}\",\n                    exit.map(|status| format!(\"：{status}\")).unwrap_or_default()\n                );\n            };\n            let line = line.trim();\n            if line.is_empty() {\n                continue;\n            }\n            match serde_json::from_str(line) {\n                Ok(message) => return Ok(message),\n                Err(_) => continue,\n            }\n        }\n    }\n\n    async fn reject_server_request(&mut self, message: &Value) -> anyhow::Result<()> {\n        let Some(id) = message.get(\"id\") else {\n            return Ok(());","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/connect/app_server.rs#L359-L395","documentation":"The Codex app-server child process closed its stdout stream while the library was reading a JSON-RPC message from it, so the pending read/turn cannot continue. The library marks the connection as no longer running and, when the OS reports the exit status, appends it (e.g. '：exit status: 101') to the message. This is thrown from read_message, the single read path used by both request() and run_turn(), so any turn in flight at process death surfaces this error.","triggerScenarios":"Calling request() or run_turn() while the spawned `codex app-server` child process has exited or crashes mid-response: the child terminates (panic, bad flag, OOM kill, manual kill) and tokio's next_line() on stdout returns None.","commonSituations":"The codex binary panics or segfaults during a long turn; the process is killed by the OS (OOM) or by the user; an incompatible codex version crashes on an unsupported request; the child was never launched successfully and died immediately before the first read.","solutions":["Check the exit-status suffix in the message and run `codex app-server` manually to reproduce the crash and see stderr","Re-run the connect flow to respawn the app-server process (the flag `running` is set false, so a restart is required)","Verify the codex binary version matches what CodexPlusPlus expects; upgrade or downgrade codex","Check system resources (memory pressure, OOM-killer logs in dmesg/journalctl) for SIGKILL of the child"],"exampleFix":"// before\n// assuming the AppServerConnection is still usable after an error\nlet reply = conn.request(params).await?;\n// after\n// detect the closed server and respawn before retrying\nmatch conn.request(params).await {\n    Err(e) if e.to_string().contains(\"Codex app-server 已关闭\") => {\n        let mut conn = AppServerConnection::spawn(&codex_path)?;\n        let reply = conn.request(params).await?;\n    }\n    other => other?,\n}","handlingStrategy":"try-catch","validationCode":"// before each turn, confirm the child is still alive\nfn server_alive(conn: &AppServerConnection) -> bool { conn.is_running() }","typeGuard":"fn ensure_running(conn: &AppServerConnection) -> anyhow::Result<()> {\n    if !conn.is_running() { anyhow::bail!(\"app-server 未运行，请重新启动连接\"); }\n    Ok(())\n}","tryCatchPattern":"match conn.request(params).await {\n    Err(e) if e.to_string().contains(\"Codex app-server 已关闭\") => respawn_and_retry(),\n    other => other,\n}","preventionTips":["Check `running`/liveness before issuing each turn","Monitor child stderr and exit events to diagnose crashes early","Pin a known-good codex binary version","Watch for OOM kills if turns are long/large"],"tags":["process-crash","ipc","rust","tokio"],"backgroundTag":"connection-refused","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"}