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

post failed ({status}): {body}

Error message

post failed ({status}): {body}

What it means

A shared POST helper used by `Channel::send` (and other call paths) bails when the HTTP status is not success, including the status and the response body. For sends, the usual meanings are: 403 the bot is not in the target channel, 401 bad/expired token or session, 404 wrong channel/user ID, 400 payload rejected.

Source

Thrown at crates/zeroclaw-channels/src/mattermost.rs:740

            );
        }

        let token = self.token().await?;
        let resp = self
            .http_client()
            .post(format!("{}/api/v4/posts", self.base_url))
            .bearer_auth(token)
            .json(&body_map)
            .send()
            .await?;

        let status = resp.status();
        if !status.is_success() {
            let body = resp
                .text()
                .await
                .unwrap_or_else(|e| format!("<failed to read response: {e}>"));
            bail!("post failed ({status}): {body}");
        }

        Ok(())
    }

    async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> Result<()> {
        match self.listen_mode {
            MattermostListenMode::Polling => self.listen_polling(tx).await,
            MattermostListenMode::Websocket => self.listen_websocket(tx).await,
        }
    }

    async fn health_check(&self) -> bool {
        let Ok(token) = self.token().await else {
            return false;
        };
        self.http_client()
            .get(format!("{}/api/v4/users/me", self.base_url))

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the status and body embedded in the message — Mattermost's body states the reason
  2. 403: add the bot to the channel via `POST /api/v4/channels/{channel_id}/members`
  3. 401: refresh `bot_token` or let the channel re-login
  4. 404: correct the channel ID or recipient (`@username` / user ID)
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = mm_channel.send(message).await {
    let msg = e.to_string();
    if msg.starts_with("post failed (403") {
        // join channel then retry once
    } else if msg.starts_with("post failed (401") {
        // refresh token, do not retry with the same session
    }
}

Prevention

When it happens

Trigger: Mattermost `send()` posting to a channel the bot has not joined, a typo'd channel or recipient ID, a token revoked since login, or content the server rejects.

Common situations: Bot removed from the channel between discovery and send; channel archived; long-lived session token expired mid-run; message exceeds server limits.

Related errors


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