{"record":{"id":"9dd31b5420ba37d0","repo":"zeroclaw-labs/zeroclaw","slug":"wecom-api-error-errcode-errcode-errmsg","errorCode":null,"errorMessage":"WeCom API error (errcode={errcode}): {errmsg}","messagePattern":"WeCom API error \\(errcode=(.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/wecom.rs","lineNumber":105,"sourceCode":"            .json(&body)\n            .send()\n            .await?;\n\n        if !resp.status().is_success() {\n            let status = resp.status();\n            let err = resp.text().await.unwrap_or_default();\n            anyhow::bail!(\"WeCom webhook send failed ({status}): {err}\");\n        }\n\n        // WeCom returns {\"errcode\":0,\"errmsg\":\"ok\"} on success.\n        let result: serde_json::Value = resp.json().await?;\n        let errcode = result.get(\"errcode\").and_then(|v| v.as_i64()).unwrap_or(-1);\n        if errcode != 0 {\n            let errmsg = result\n                .get(\"errmsg\")\n                .and_then(|v| v.as_str())\n                .unwrap_or(\"unknown error\");\n            anyhow::bail!(\"WeCom API error (errcode={errcode}): {errmsg}\");\n        }\n\n        Ok(())\n    }\n\n    async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {\n        ::zeroclaw_log::record!(\n            INFO,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),\n            \"channel ready (send-only via Bot Webhook)\"\n        );\n        tx.closed().await;\n        Ok(())\n    }\n\n    async fn health_check(&self) -> bool {\n        // Verify we can reach the WeCom API endpoint.\n        let resp = self","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/wecom.rs#L87-L123","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nfor m in burst { channel.send(m).await?; } // trips 45009 frequency limit\n// after\nfor m in burst {\n    channel.send(m).await?;\n    tokio::time::sleep(Duration::from_secs(3)).await; // stay under robot rate limit\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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"],"tags":["wecom","webhook","errcode","api-response","rate-limit"],"backgroundTag":"api-error-code-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}