{"record":{"id":"cb0fdeee1292ca31","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-app-server-method","errorCode":null,"errorMessage":"Codex app-server {method} 请求超时","messagePattern":"Codex app-server (.+?) 请求超时","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/connect/app_server.rs","lineNumber":275,"sourceCode":"    async fn request(\n        &mut self,\n        method: &str,\n        params: Value,\n        timeout: Duration,\n    ) -> anyhow::Result<Value> {\n        let id = self.take_request_id();\n        self.write_json(&json!({\n            \"jsonrpc\": \"2.0\",\n            \"id\": id,\n            \"method\": method,\n            \"params\": params\n        }))\n        .await?;\n        let deadline = tokio::time::Instant::now() + timeout;\n        loop {\n            let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());\n            if remaining.is_zero() {\n                bail!(\"Codex app-server {method} 请求超时\");\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                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 {","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-core/src/connect/app_server.rs#L257-L293","documentation":"request() is the generic JSON-RPC call used for initialize and thread/start or thread/resume, each bounded by REQUEST_TIMEOUT. This error fires when no response with the matching request id arrives before the deadline; server-request notifications are rejected and skipped and unrelated ids ignored, so only a missing or late answer triggers it.","triggerScenarios":"initialize on a cold codex app-server that boots slowly (first-run migrations, slow disk); thread/resume against a large archived thread; the app-server is alive but stuck, for example waiting on auth.","commonSituations":"First launch after install or upgrade; HOME on network storage; codex not logged in so the app-server stalls; underpowered machine under load.","solutions":["Run codex once manually so first-run setup completes outside the connector","Complete codex login before starting the connector","Retry the connect after the app-server settles; pre-warm it on boot if this recurs","If it reproduces reliably, capture app-server stderr and raise REQUEST_TIMEOUT locally as a stopgap"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = match server.request(method, params, REQUEST_TIMEOUT).await {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"请求超时\") => {\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        server.request(method, params, REQUEST_TIMEOUT).await?\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["Warm the app-server once at startup before first use","Complete codex login ahead of connecting","Avoid HOME on slow network storage for the app-server workspace"],"tags":["timeout","jsonrpc","codex","initialize"],"backgroundTag":"jsonrpc-request-timeout","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"}