{"record":{"id":"717b48e5bd7ac335","repo":"zeroclaw-labs/zeroclaw","slug":"mochat-send-message-failed-status-err","errorCode":null,"errorMessage":"Mochat send message failed ({status}): {err}","messagePattern":"Mochat send message failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/mochat.rs","lineNumber":121,"sourceCode":"            \"toUserId\": message.recipient,\n            \"msgType\": \"text\",\n            \"content\": {\n                \"text\": message.content,\n            }\n        });\n\n        let resp = self\n            .http_client()\n            .post(format!(\"{}/api/message/send\", self.api_url))\n            .header(\"Authorization\", format!(\"Bearer {}\", self.api_token))\n            .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!(\"Mochat send message failed ({status}): {err}\");\n        }\n\n        let result: serde_json::Value = resp.json().await?;\n        let code = result.get(\"code\").and_then(|v| v.as_i64()).unwrap_or(-1);\n        if code != 0 && code != 200 {\n            let msg = result\n                .get(\"msg\")\n                .or_else(|| result.get(\"message\"))\n                .and_then(|v| v.as_str())\n                .unwrap_or(\"unknown error\");\n            anyhow::bail!(\"Mochat API error (code={code}): {msg}\");\n        }\n\n        Ok(())\n    }\n\n    async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {\n        ::zeroclaw_log::record!(","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/mochat.rs#L103-L139","documentation":"`MochatChannel::send` POSTs to `<api_url>/api/message/send` with a Bearer token and bails when the HTTP status is not success, echoing the status and body. This is a transport/auth-level failure (401 bad api_token, 404 wrong api_url, 5xx gateway/server down), distinct from the in-body business `code` error raised after this check.","triggerScenarios":"Any `send`, in-listen reply, or `health_check` with an invalid/expired api_token, a wrong api_url base path, or the MoChat gateway being down or returning 502/503.","commonSituations":"api_token expired or copied incorrectly; api_url missing scheme/host or pointing at the wrong service; gateway behind a proxy that returns 5xx during outages.","solutions":["Map the status: 401 → fix api_token; 404 → fix api_url; 5xx → MoChat server side","Probe the gateway: `curl -H \"Authorization: Bearer <token>\" <api_url>/api/message/receive`","Read the `err` body text in the message — gateways usually include a reason"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let resp = client\n    .get(format!(\"{api_url}/api/message/receive\"))\n    .header(\"Authorization\", format!(\"Bearer {api_token}\"))\n    .send()\n    .await?;\nif !resp.status().is_success() {\n    return Err(anyhow::anyhow!(\"mochat unreachable or token invalid\"));\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = mochat.send(msg).await {\n    let text = e.to_string();\n    if text.starts_with(\"Mochat send message failed (401\") {\n        // rotate api_token\n    } else if text.starts_with(\"Mochat send message failed (5\") {\n        // gateway outage: retry with backoff\n    }\n}","preventionTips":["Health-check the gateway with a cheap GET before enabling the channel","Keep api_url and api_token in one config block validated at startup","Distinguish HTTP failures from body-code failures when routing alerts"],"tags":["mochat","send","http-status","auth","rust"],"backgroundTag":"http-error-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}