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

Mattermost WebSocket authentication handshake timed out

Error message

Mattermost WebSocket authentication handshake timed out

What it means

During the Mattermost WebSocket handshake, `authenticate_websocket` waits (in a `tokio::select!` against a deadline) for both the auth acknowledgement and the `hello` event. If the deadline fires first, this timeout error is raised: the TCP/WS connection was established but the server never completed the authentication exchange.

Source

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

            "data": { "token": token }
        });
        write
            .send(WsMessage::Text(auth.to_string().into()))
            .await
            .context("Mattermost WebSocket authentication send failed")?;

        let deadline = tokio::time::Instant::now() + timeout;
        let mut authenticated = false;
        let mut server_version = None;

        loop {
            if authenticated && server_version.is_some() {
                return Ok(server_version.unwrap_or_else(|| "unknown".to_string()));
            }

            tokio::select! {
                _ = tokio::time::sleep_until(deadline) => {
                    bail!("Mattermost WebSocket authentication handshake timed out");
                }
                frame = read.next() => {
                    let text = match frame {
                        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}");
                        }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Retry the connection — transient stalls during deploys are the norm
  2. Verify raw WebSocket reachability: `wscat -H "Authorization: Bearer <token>" <server>/api/v4/websocket`
  3. Fix proxy config: pass Upgrade/Connection headers and disable response buffering for /api/v4/websocket
  4. Confirm `base_url` points at the Mattermost server, not an HTML-serving front door
Defensive patterns

Strategy: retry

Try / catch

let mut backoff = std::time::Duration::from_secs(1);
loop {
    match mm_channel.listen(tx.clone()).await {
        Ok(()) => break Ok(()),
        Err(e) if e.to_string().contains("authentication handshake timed out") => {
            tokio::time::sleep(backoff).await;
            backoff = (backoff * 2).min(std::time::Duration::from_secs(60));
        }
        Err(e) => break Err(e),
    }
}

Prevention

When it happens

Trigger: `listen_websocket` connects and sends the auth challenge, but neither the `hello` event nor the auth reply arrives before the deadline — hung server, reverse proxy buffering WebSocket frames, or a stalled network path.

Common situations: nginx/traefik without WebSocket upgrade configuration; load balancer with very short timeouts for new connections; Mattermost restarting mid-handshake; heavily loaded server.

Understand the failure class

Related errors


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