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
- Retry the connection — transient stalls during deploys are the norm
- Verify raw WebSocket reachability: `wscat -H "Authorization: Bearer <token>" <server>/api/v4/websocket`
- Fix proxy config: pass Upgrade/Connection headers and disable response buffering for /api/v4/websocket
- 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
- Always run channel listeners under a supervised reconnect loop with backoff
- Verify WebSocket proxying (Upgrade/Connection headers, no buffering) before deploying
- Add a pre-deploy WS smoke test with wscat against /api/v4/websocket
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Mattermost WebSocket closed during authentication: {reason}
- Mattermost WebSocket ended during authentication
- Mattermost WebSocket authentication was rejected
- Mattermost WebSocket stream ended
- Mattermost WebSocket closed: {reason}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/8e8d8c9c9e2a34e3.
Report an issue: GitHub.