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

Mattermost WebSocket ended during authentication

Error message

Mattermost WebSocket ended during authentication

What it means

During WebSocket authentication the read stream returned `None` — the connection ended without a Close frame. The socket vanished silently, which typically means a dropped TCP connection or an intermediary cutting it, rather than a server-initiated close.

Source

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

                        Some(Ok(WsMessage::Text(text))) => text,
                        Some(Ok(WsMessage::Ping(payload))) => {
                            write
                                .send(WsMessage::Pong(payload))
                                .await
                                .context("Mattermost WebSocket handshake pong failed")?;
                            continue;
                        }
                        Some(Ok(WsMessage::Close(frame))) => {
                            let reason = frame
                                .as_ref()
                                .map(|frame| frame.reason.as_ref())
                                .unwrap_or("");
                            bail!("Mattermost WebSocket closed during authentication: {reason}");
                        }
                        Some(Err(error)) => {
                            return Err(error).context("Mattermost WebSocket handshake read failed");
                        }
                        None => bail!("Mattermost WebSocket ended during authentication"),
                        Some(Ok(_)) => continue,
                    };

                    let event: serde_json::Value = serde_json::from_str(text.as_ref())
                        .context("Mattermost WebSocket handshake returned invalid JSON")?;

                    if let Some(ok) = Self::ws_auth_response(&event, auth_seq) {
                        if !ok {
                            bail!("Mattermost WebSocket authentication was rejected");
                        }
                        authenticated = true;
                    }

                    if event.get("event").and_then(|value| value.as_str()) == Some("hello") {
                        server_version = Some(
                            event
                                .get("data")
                                .and_then(|data| data.get("server_version"))

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Retry with backoff — most silent ends during handshake are transient
  2. Test TCP stability to the Mattermost host (long curl or mtr)
  3. Check proxy idle-timeout and reset behavior on new WebSocket connections
Defensive patterns

Strategy: retry

Try / catch

Err(e) if e.to_string().contains("ended during authentication") => {
    // silent stream end: reconnect with backoff
    tokio::time::sleep(backoff.next()).await;
    continue;
}

Prevention

When it happens

Trigger: During `authenticate_websocket`: network drop, NAT/firewall reset, proxy killing the fresh socket, or the Mattermost process dying before the handshake completes.

Common situations: Flaky networks and VPNs; containers restarting mid-connect; aggressive connection-level firewall rules.

Understand the failure class

Related errors


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