zeroclaw-labs/zeroclaw · error
WeCom API error (errcode={errcode}): {errmsg}
Error message
WeCom API error (errcode={errcode}): {errmsg} What it means
WeCom webhooks answer HTTP 200 with {"errcode":N,"errmsg":"..."}; errcode != 0 is a failure and bails with both values. A missing errcode field parses as -1, so a malformed or non-JSON body also trips this with errcode=-1 and errmsg='unknown error'.
Source
Thrown at crates/zeroclaw-channels/src/wecom.rs:105
.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!(
INFO,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),
"channel ready (send-only via Bot Webhook)"
);
tx.closed().await;
Ok(())
}
async fn health_check(&self) -> bool {
// Verify we can reach the WeCom API endpoint.
let resp = selfView on GitHub (pinned to 88bb9c8533)
Solutions
- Match the errcode against WeCom webhook docs: 45002 → shorten the message; 45009 → throttle outbound sends and retry; key errors → regenerate the webhook URL and update config
- For errcode=-1 with odd errmsg, dump the raw response — something between the bot and WeCom mangled the body
- Add per-robot send throttling to stay under frequency limits
- Retry once after the appropriate backoff, then dead-letter
Example fix
// before
for m in burst { channel.send(m).await?; } // trips 45009 frequency limit
// after
for m in burst {
channel.send(m).await?;
tokio::time::sleep(Duration::from_secs(3)).await; // stay under robot rate limit
} Defensive patterns
Strategy: try-catch
Try / catch
Extract errcode from the message via 'errcode=(\d+)' and branch: 45009 → sleep past the rate window and retry once; 45002 → chunk and resend; key errors → disable channel; errcode=-1 → log the raw body (proxy interference) and do not retry.
Prevention
- Throttle sends below the WeCom robot frequency limit (about 20 messages/minute per robot)
- Validate message size before sending
- Handle errcode values explicitly instead of treating every failure the same
When it happens
Trigger: send()/health_check() returning errcode such as 45002 (message content over the length limit), 45009 (send frequency limit hit), an invalid-key error, or errcode=-1 because the response body was not the expected JSON envelope.
Common situations: Bot bursting past the WeCom robot rate limit; markdown/text over the content limit; stale or revoked webhook key; a proxy/WAF returning HTML that still yields HTTP 200.
Related errors
- sendMessage failed ({err})
- WeCom webhook send failed ({status}): {err}
- channel does not support room creation
- post failed ({status}): {body}
- webhook reply failed ({status}): {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/9dd31b5420ba37d0.
Report an issue: GitHub.