BigPizzaV3/CodexPlusPlus · error · anyhow::Error
发送微信回复被拒绝:ret={} errcode={} {}
Error message
发送微信回复被拒绝:ret={} errcode={} {} What it means
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.
Source
Thrown at crates/codex-plus-core/src/connect/weixin.rs:306
.post(self.endpoint("ilink/bot/sendmessage")?)
.headers(self.auth_headers()?)
.json(&request_body)
.timeout(Duration::from_secs(15))
.send()
.await
.context("发送微信回复失败")?;
let (http_status, bytes) =
read_response_limited(response, MAX_SMALL_RESPONSE_BYTES, "微信发送响应").await?;
if !http_status.is_success() {
bail!("发送微信回复失败:HTTP {http_status}");
}
if bytes.iter().all(u8::is_ascii_whitespace) {
return Ok(());
}
let result: WeixinSendResponse =
serde_json::from_slice(&bytes).context("微信发送响应格式无效")?;
if result.ret != 0 || result.errcode != 0 {
bail!(
"发送微信回复被拒绝:ret={} errcode={} {}",
result.ret,
result.errcode,
result.errmsg
);
}
Ok(())
}
fn endpoint(&self, path: &str) -> anyhow::Result<reqwest::Url> {
let base = format!("{}/", self.base_url.trim_end_matches('/'));
reqwest::Url::parse(&base)?
.join(path.trim_start_matches('/'))
.context("微信 API 地址无效")
}
fn auth_headers(&self) -> anyhow::Result<HeaderMap> {
let mut headers = self.route_headers(false)?;View on GitHub (pinned to f2074595a2)
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
Defensive patterns
Strategy: retry
Try / catch
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.
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- {label}超过大小限制
- invalid Dream Skin destination name
- Dream Skin image is not a file
- Dream Skin image is empty
- Dream Skin source image exceeds 50 MiB
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@f2074595a2 (2026-08-23).
Data as JSON: /api/errors/3541cf5f772528ca.
Report an issue: GitHub.