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

Mochat API error (code={code}): {msg}

Error message

Mochat API error (code={code}): {msg}

What it means

MoChat answers HTTP 200 but carries a business status in the JSON body; this error fires when that `code` is anything other than 0 or 200. The `msg`/`message` field from the body is included (defaulting to "unknown error" when absent). Typical causes are an unknown `toUserId` recipient or content the gateway refuses.

Source

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

            .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!(
            INFO,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),
            "starting message poller"
        );

        let poll_interval = std::time::Duration::from_secs(self.poll_interval_secs);
        let mut last_message_id: Option<String> = None;

        loop {
            let mut url = format!("{}/api/message/receive", self.api_url);
            if let Some(ref id) = last_message_id {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the `msg` — it states the business failure verbatim from the gateway
  2. Verify the recipient's toUserId exists in the MoChat system
  3. Look up the numeric code in the MoChat gateway API docs
  4. Retry only when the msg indicates a transient condition
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = mochat.send(msg).await {
    let text = e.to_string();
    if text.starts_with("Mochat API error") {
        // business rejection: surface the msg text to the user; do not blind-retry
    }
}

Prevention

When it happens

Trigger: `Channel::send` where the HTTP layer succeeded but the body `code` is e.g. a recipient error: `toUserId` (message.recipient) does not exist, is not provisioned, or the content violates a gateway rule.

Common situations: Recipient user ID typo'd; recipient not registered in MoChat; gateway limits (length, rate) expressed as business codes.

Related errors


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