{"record":{"id":"809d0a561d7f6c7c","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-turn-start-error","errorCode":null,"errorMessage":"Codex turn/start 失败：{error}","messagePattern":"Codex turn/start 失败：(.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/connect/app_server.rs","lineNumber":154,"sourceCode":"        let mut response_received = false;\n        let mut turn_completed = false;\n        let mut reply_parts = Vec::new();\n        let mut model = self.config.model.trim().to_string();\n        let mut usage = TurnUsage::default();\n        let deadline = tokio::time::Instant::now() + TURN_TIMEOUT;\n        while !response_received || !turn_completed {\n            let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());\n            if remaining.is_zero() {\n                bail!(\"等待 Codex 回复超时\");\n            }\n            let message = self.read_message(remaining).await?;\n            if is_server_request(&message) {\n                self.reject_server_request(&message).await?;\n                continue;\n            }\n            if response_id(&message) == Some(id) {\n                if let Some(error) = rpc_error(&message) {\n                    bail!(\"Codex turn/start 失败：{error}\");\n                }\n                let result = message.get(\"result\").cloned().unwrap_or(Value::Null);\n                if extract_turn_id(&result).is_none() {\n                    bail!(\"Codex turn/start 未返回 turn id\");\n                }\n                if model.is_empty() {\n                    model = extract_model(&result).unwrap_or_default();\n                }\n                if let Some(reported_usage) = extract_turn_usage(&result) {\n                    usage = reported_usage;\n                }\n                response_received = true;\n                continue;\n            }\n\n            if model.is_empty() {\n                model = extract_model(&message).unwrap_or_default();\n            }","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-core/src/connect/app_server.rs#L136-L172","documentation":"The JSON-RPC response to turn/start carried an error object, and run_turn rethrows its message verbatim after the prefix. The text after the colon is whatever the Codex app-server reported, typically an invalid thread id, an unknown model, or a rejected parameter.","triggerScenarios":"run_turn(thread_id, prompt) where thread_id no longer exists on the server (session rolled or deleted); config.model names a model the installed codex does not support; the params shape is rejected by a newer or older app-server.","commonSituations":"Persisted thread_ids from a previous codex version; model renamed after an upgrade while the profile still lists the old slug; app-server and codex-plus-core version skew.","solutions":["Read the appended server text, it names the exact rejected field or id","Drop the saved thread id and start a fresh thread via thread/start, as process_weixin_message already does on resume failure","Verify config.model against the models the installed codex exposes","Keep codex-plus-core and the codex app-server on matched versions"],"exampleFix":"// before\nlet result = server.run_turn(&thread_id, &text).await?;\n// after: expired thread id, start a fresh thread and retry once\nlet result = match server.run_turn(&thread_id, &text).await {\n    Ok(result) => result,\n    Err(error) if error.to_string().contains(\"turn/start 失败\") => {\n        let fresh = server.prepare_thread(None).await?;\n        server.run_turn(&fresh, &text).await?\n    }\n    Err(error) => return Err(error),\n};","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match server.run_turn(&thread_id, prompt).await {\n    Ok(r) => r,\n    Err(e) if e.to_string().contains(\"turn/start 失败\") => {\n        state.thread_ids.remove(&peer);\n        let fresh = server.prepare_thread(None).await?;\n        server.run_turn(&fresh, prompt).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Store thread ids keyed per peer and clear them on any turn/start error","Validate config.model against the installed codex before use","Upgrade codex and codex-plus-core together"],"tags":["jsonrpc","codex","rpc-error","turn"],"backgroundTag":"jsonrpc-error-response","analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}