zeroclaw-labs/zeroclaw · error · anyhow::Error

WeCom webhook send failed ({status}): {err}

Error message

WeCom webhook send failed ({status}): {err}

What it means

The WeCom webhook channel POSTs the message payload to the robot's webhook URL; a non-2xx HTTP status bails with the status and body. It is raised in send(), which both outbound dispatch and health_check exercise, so a bad webhook surfaces immediately on health probes.

Source

Thrown at crates/zeroclaw-channels/src/wecom.rs:94

    async fn send(&self, message: &SendMessage) -> anyhow::Result<()> {
        let body = serde_json::json!({
            "msgtype": "text",
            "text": {
                "content": message.content,
            }
        });

        let resp = self
            .http_client()
            .post(self.webhook_url())
            .json(&body)
            .send()
            .await?;

        if !resp.status().is_success() {
            let status = resp.status();
            let err = resp.text().await.unwrap_or_default();
            anyhow::bail!("WeCom webhook send failed ({status}): {err}");
        }

        // WeCom returns {"errcode":0,"errmsg":"ok"} on success.
        let result: serde_json::Value = resp.json().await?;
        let errcode = result.get("errcode").and_then(|v| v.as_i64()).unwrap_or(-1);
        if errcode != 0 {
            let errmsg = result
                .get("errmsg")
                .and_then(|v| v.as_str())
                .unwrap_or("unknown error");
            anyhow::bail!("WeCom API error (errcode={errcode}): {errmsg}");
        }

        Ok(())
    }

    async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {
        ::zeroclaw_log::record!(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Verify the full webhook URL including ?key=... matches the currently issued robot webhook
  2. Act on the status: 404 → wrong URL/key; 413/payload errors → shorten the message; 5xx → retry later
  3. Read the embedded body — WeCom gateway errors are explicit about the cause
  4. Fix config before queueing more sends; use health_check to confirm the fix

Example fix

// before
let m = ChannelMessage::markdown(&scope, &huge_report); // gateway rejects size
// after
for chunk in split_markdown(&huge_report, 2000) {
    channel.send(ChannelMessage::markdown(&scope, chunk)).await?;
}
Defensive patterns

Strategy: retry

Try / catch

Parse the embedded status from 'WeCom webhook send failed': 404 → disable the channel and alert (bad key/URL); 413 → chunk the payload and resend; 5xx → retry with backoff, then dead-letter.

Prevention

When it happens

Trigger: Calling send() or health_check() when the WeCom webhook endpoint answers 404 (wrong or rotated key in the URL), 413 (payload too large), or 5xx (WeCom gateway error).

Common situations: Webhook key regenerated in the WeCom admin console but not updated in channel config; copied URL truncated; markdown/text content exceeding WeCom's per-message size limit rejected at the gateway; WeCom API outage.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/17e440ac86941348. Report an issue: GitHub.