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

Mochat send message failed ({status}): {err}

Error message

Mochat send message failed ({status}): {err}

What it means

`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.

Source

Thrown at crates/zeroclaw-channels/src/mochat.rs:121

            "toUserId": message.recipient,
            "msgType": "text",
            "content": {
                "text": message.content,
            }
        });

        let resp = self
            .http_client()
            .post(format!("{}/api/message/send", self.api_url))
            .header("Authorization", format!("Bearer {}", self.api_token))
            .json(&body)
            .send()
            .await?;

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

        let result: serde_json::Value = resp.json().await?;
        let code = result.get("code").and_then(|v| v.as_i64()).unwrap_or(-1);
        if code != 0 && code != 200 {
            let msg = result
                .get("msg")
                .or_else(|| result.get("message"))
                .and_then(|v| v.as_str())
                .unwrap_or("unknown error");
            anyhow::bail!("Mochat API error (code={code}): {msg}");
        }

        Ok(())
    }

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

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Map the status: 401 → fix api_token; 404 → fix api_url; 5xx → MoChat server side
  2. Probe the gateway: `curl -H "Authorization: Bearer <token>" <api_url>/api/message/receive`
  3. Read the `err` body text in the message — gateways usually include a reason
Defensive patterns

Strategy: try-catch

Validate before calling

let resp = client
    .get(format!("{api_url}/api/message/receive"))
    .header("Authorization", format!("Bearer {api_token}"))
    .send()
    .await?;
if !resp.status().is_success() {
    return Err(anyhow::anyhow!("mochat unreachable or token invalid"));
}

Try / catch

if let Err(e) = mochat.send(msg).await {
    let text = e.to_string();
    if text.starts_with("Mochat send message failed (401") {
        // rotate api_token
    } else if text.starts_with("Mochat send message failed (5") {
        // gateway outage: retry with backoff
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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