{"record":{"id":"9c7881c1f2e9aaf2","repo":"zeroclaw-labs/zeroclaw","slug":"sendmessage-failed-err","errorCode":null,"errorMessage":"sendMessage failed ({err})","messagePattern":"sendMessage failed \\((.+?)\\)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/wechat.rs","lineNumber":1965,"sourceCode":"            .json(&body)\n            .timeout(API_TIMEOUT)\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!(\"sendMessage failed ({status}): {err}\");\n        }\n\n        // The API reports failures as HTTP 200 with a non-zero ret/errcode\n        // in the body; a status check alone silently drops the message.\n        let body = resp\n            .text()\n            .await\n            .context(\"failed to read sendMessage response body\")?;\n        if let Some(err) = sendmessage_body_error(&body) {\n            anyhow::bail!(\"sendMessage failed ({err})\");\n        }\n\n        Ok(())\n    }\n\n    /// Send a text message via iLink API.\n    async fn send_text(\n        &self,\n        to: &str,\n        text: &str,\n        context_token: Option<&str>,\n    ) -> anyhow::Result<()> {\n        self.send_message_items(\n            to,\n            vec![serde_json::json!({\n                \"type\": ITEM_TYPE_TEXT,\n                \"text_item\": { \"text\": markdown_to_plain_text(text) }\n            })],","sourceCodeStart":1947,"sourceCodeEnd":1983,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/wechat.rs#L1947-L1983","documentation":"iLink reports sendMessage failures as HTTP 200 with a non-zero ret or errcode inside the JSON body. After reading the body, the channel runs sendmessage_body_error() (wechat.rs:454) and bails with the extracted error string; without this check failures would be silently dropped. Known examples from the crate's tests include 'ret:-1, context token expired' and 'errcode:301, session expired'.","triggerScenarios":"An HTTP-200 sendMessage response whose JSON body has ret != 0 or errcode != 0 — session/token expiry surfaced in-band, invalid chat target, blocked recipient, or content rejected upstream.","commonSituations":"iLink session expiring while HTTP still succeeds; sending to a chat the bot left or was removed from; recipient blocked the bot; middleware or version drift changing the error body shape (empty or non-JSON bodies are treated as legacy success and do NOT trigger this).","solutions":["Parse the embedded error text: token/session errors (e.g. 'context token expired', 'session expired') → re-login and resend once; target-invalid → drop and log; content rejection → adjust the payload","Re-authenticate the iLink session before retrying when the error mentions tokens or sessions","Dead-letter the message after a bounded number of in-band failures instead of blind-retrying","If the error text looks like HTML or is empty, suspect a proxy mangling responses"],"exampleFix":"// before\nchannel.send(msg).await?; // HTTP 200 hides ret=-1 'context token expired'\n// after\nif let Err(e) = channel.send(msg.clone()).await {\n    if e.to_string().contains(\"token expired\") || e.to_string().contains(\"session expired\") {\n        channel.relogin().await?;\n        channel.send(msg).await?;\n    } else { return Err(e); }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"Catch the send error and branch on the trailing err text: token/session errors → re-login and resend once; target-invalid/blocked → drop and log; unknown → dead-letter with the raw body attached for diagnosis.","preventionTips":["Validate chat targets against the known contact/chat list before sending","Treat HTTP 200 as unproven — always surface in-band ret/errcode failures","Log full error strings to build a retry/no-retry decision table for iLink codes"],"tags":["wechat","send-message","errcode","api-response","outbound"],"backgroundTag":"api-error-code-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}