{"record":{"id":"3541cf5f772528ca","repo":"BigPizzaV3/CodexPlusPlus","slug":"ret-errcode-3541cf","errorCode":null,"errorMessage":"发送微信回复被拒绝：ret={} errcode={} {}","messagePattern":"发送微信回复被拒绝：ret=(.+?) errcode=(.+?) (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/connect/weixin.rs","lineNumber":306,"sourceCode":"            .post(self.endpoint(\"ilink/bot/sendmessage\")?)\n            .headers(self.auth_headers()?)\n            .json(&request_body)\n            .timeout(Duration::from_secs(15))\n            .send()\n            .await\n            .context(\"发送微信回复失败\")?;\n        let (http_status, bytes) =\n            read_response_limited(response, MAX_SMALL_RESPONSE_BYTES, \"微信发送响应\").await?;\n        if !http_status.is_success() {\n            bail!(\"发送微信回复失败：HTTP {http_status}\");\n        }\n        if bytes.iter().all(u8::is_ascii_whitespace) {\n            return Ok(());\n        }\n        let result: WeixinSendResponse =\n            serde_json::from_slice(&bytes).context(\"微信发送响应格式无效\")?;\n        if result.ret != 0 || result.errcode != 0 {\n            bail!(\n                \"发送微信回复被拒绝：ret={} errcode={} {}\",\n                result.ret,\n                result.errcode,\n                result.errmsg\n            );\n        }\n        Ok(())\n    }\n\n    fn endpoint(&self, path: &str) -> anyhow::Result<reqwest::Url> {\n        let base = format!(\"{}/\", self.base_url.trim_end_matches('/'));\n        reqwest::Url::parse(&base)?\n            .join(path.trim_start_matches('/'))\n            .context(\"微信 API 地址无效\")\n    }\n\n    fn auth_headers(&self) -> anyhow::Result<HeaderMap> {\n        let mut headers = self.route_headers(false)?;","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-core/src/connect/weixin.rs#L288-L324","documentation":"WeixinClient::send_text posts the reply to the WeChat ilink bot endpoint 'ilink/bot/sendmessage' with the bot Bearer token and the inbound message's context_token. HTTP succeeded, but the parsed WeixinSendResponse has ret != 0 or errcode != 0, so WeChat rejected the reply at the business level; errmsg carries the server's reason.","triggerScenarios":"Calling send_text_chunks with a context_token that expired or was already consumed; a revoked or stale bot token (bot logged out elsewhere); replying long after the inbound message was polled; WeChat frequency control; a to_user_id that does not match the context_token.","commonSituations":"A multi-minute LLM generation outliving the context_token validity window; the operator re-scanned the QR login while the process kept the old token; automatic retries re-sending with the same already-used context_token.","solutions":["Poll get_updates again and reply with the fresh context_token from the newest inbound message","If every send is rejected, re-run the QR login flow - the bot token itself is stale","Log ret/errcode/errmsg and back off when errmsg signals rate limiting instead of retrying immediately","Never reuse a context_token after a successful send"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Downcast the anyhow::Error to its message and test for the prefix '发送微信回复被拒绝'. On hit: fetch the next update for a fresh context_token and retry once; if the retry also fails, mark the WeChat session stale and prompt re-login instead of looping.","preventionTips":["Reply promptly after polling - context tokens are short-lived and single-use","A burst of rejections across all users means a stale bot token, not bad per-message input","Keep the 100 ms inter-chunk pacing from send_text_chunks; do not parallelize sends","Surface errmsg in operator logs - WeChat's frequency-control messages are explicit"],"tags":["wechat","api-rejection","bot-messaging","rust"],"backgroundTag":"wechat-api-error","analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}