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

Mattermost WebSocket closed: {reason}

Error message

Mattermost WebSocket closed: {reason}

What it means

The server sent a WebSocket Close frame during an established listening session; the reason string is included. This is the normal shutdown path for the protocol: server restarts, session invalidation, or proxy closes. The error exists so the listener can distinguish close-with-reason from silent drops and reconnect.

Source

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

                        }
                        None => bail!("Mattermost WebSocket stream ended"),
                    };

                    let text = match frame {
                        WsMessage::Text(text) => text,
                        WsMessage::Ping(payload) => {
                            write
                                .send(WsMessage::Pong(payload))
                                .await
                                .context("Mattermost WebSocket pong send failed")?;
                            continue;
                        }
                        WsMessage::Close(frame) => {
                            let reason = frame
                                .as_ref()
                                .map(|frame| frame.reason.as_ref())
                                .unwrap_or("");
                            bail!("Mattermost WebSocket closed: {reason}");
                        }
                        _ => continue,
                    };

                    let event: serde_json::Value = match serde_json::from_str(text.as_ref()) {
                        Ok(event) => event,
                        Err(error) => {
                            ::zeroclaw_log::record!(
                                WARN,
                                ::zeroclaw_log::Event::new(
                                    module_path!(),
                                    ::zeroclaw_log::Action::Note,
                                )
                                .with_outcome(::zeroclaw_log::EventOutcome::Unknown)
                                .with_attrs(::serde_json::json!({
                                    "alias": self.alias,
                                    "error": error.to_string(),
                                })),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Treat as reconnectable: re-invoke `listen()` with backoff
  2. Inspect the close reason — auth-policy closes need a token refresh, not a retry
  3. If closes repeat rapidly, check server health and token validity
Defensive patterns

Strategy: retry

Try / catch

match mm_channel.listen(tx).await {
    Err(e) if e.to_string().contains("Mattermost WebSocket closed:") => {
        let reason = e.to_string();
        if reason.contains("auth") {
            // refresh credentials before reconnecting
        }
        // otherwise reconnect with backoff
    }
    other => other,
}

Prevention

When it happens

Trigger: `listen_websocket` receives `WsMessage::Close` mid-session: Mattermost shutdown/restart, server-side token/session invalidation, or an intermediary closing the connection.

Common situations: Server upgrades and nightly restarts; token revoked causing the server to close all its sockets; proxy idle closes.

Related errors


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