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

sendMessage failed ({status}): {err}

Error message

sendMessage failed ({status}): {err}

What it means

The WeChat iLink send path checks the HTTP status of the sendMessage request; any non-2xx bails with the status and response body. This is the transport-level send failure; the body-level ret/errcode check (error 345) only runs after this passes.

Source

Thrown at crates/zeroclaw-channels/src/wechat.rs:1955

                "item_list": item_list,
                "context_token": context_token.unwrap_or("")
            },
            "base_info": build_base_info()
        });

        let resp = self
            .client
            .post(self.api_url("sendmessage"))
            .headers(build_headers(Some(&token)))
            .json(&body)
            .timeout(API_TIMEOUT)
            .send()
            .await?;

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

        // The API reports failures as HTTP 200 with a non-zero ret/errcode
        // in the body; a status check alone silently drops the message.
        let body = resp
            .text()
            .await
            .context("failed to read sendMessage response body")?;
        if let Some(err) = sendmessage_body_error(&body) {
            anyhow::bail!("sendMessage failed ({err})");
        }

        Ok(())
    }

    /// Send a text message via iLink API.
    async fn send_text(
        &self,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Branch on the embedded status: 401 → re-login the WeChat session; 429 → back off and resend; 5xx → retry; 404 → fix api_url
  2. Retry the send with exponential backoff, but only for transient statuses (429/5xx)
  3. If every send fails, probe a cheap authenticated iLink endpoint to confirm the session is dead
  4. Capture the body text — iLink error strings usually name the exact problem

Example fix

// before
channel.send(msg).await?;
// after
let mut attempt = 0;
loop {
    match channel.send(msg.clone()).await {
        Ok(_) => break,
        Err(e) if attempt < 3 && is_transient(&e) => {
            attempt += 1;
            tokio::time::sleep(Duration::from_secs(1 << attempt)).await;
        }
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: retry

Try / catch

Inspect the embedded status: retry only 408/429/5xx with exponential backoff; escalate 401 to session re-login and resend once; 404 means config drift — stop and fix api_url.

Prevention

When it happens

Trigger: Calling send()/send_message on the WeChat channel when iLink returns 401 (session token expired), 429 (rate limited), 500/502 (server error), or 404 (wrong endpoint from a misconfigured api_url).

Common situations: Long-running bots whose iLink session token lapses; message bursts hitting iLink rate limits; iLink restarts mid-send; proxy or gateway faults.

Related errors


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