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

  1. Poll get_updates again and reply with the fresh context_token from the newest inbound message
  2. If every send is rejected, re-run the QR login flow - the bot token itself is stale
  3. Log ret/errcode/errmsg and back off when errmsg signals rate limiting instead of retrying immediately
  4. 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

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


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@f2074595a2 (2026-08-23). Data as JSON: /api/errors/3541cf5f772528ca. Report an issue: GitHub.