{"record":{"id":"8e8d8c9c9e2a34e3","repo":"zeroclaw-labs/zeroclaw","slug":"mattermost-websocket-authentication-handshake-time","errorCode":null,"errorMessage":"Mattermost WebSocket authentication handshake timed out","messagePattern":"Mattermost WebSocket authentication handshake timed out","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/mattermost.rs","lineNumber":444,"sourceCode":"            \"data\": { \"token\": token }\n        });\n        write\n            .send(WsMessage::Text(auth.to_string().into()))\n            .await\n            .context(\"Mattermost WebSocket authentication send failed\")?;\n\n        let deadline = tokio::time::Instant::now() + timeout;\n        let mut authenticated = false;\n        let mut server_version = None;\n\n        loop {\n            if authenticated && server_version.is_some() {\n                return Ok(server_version.unwrap_or_else(|| \"unknown\".to_string()));\n            }\n\n            tokio::select! {\n                _ = tokio::time::sleep_until(deadline) => {\n                    bail!(\"Mattermost WebSocket authentication handshake timed out\");\n                }\n                frame = read.next() => {\n                    let text = match frame {\n                        Some(Ok(WsMessage::Text(text))) => text,\n                        Some(Ok(WsMessage::Ping(payload))) => {\n                            write\n                                .send(WsMessage::Pong(payload))\n                                .await\n                                .context(\"Mattermost WebSocket handshake pong failed\")?;\n                            continue;\n                        }\n                        Some(Ok(WsMessage::Close(frame))) => {\n                            let reason = frame\n                                .as_ref()\n                                .map(|frame| frame.reason.as_ref())\n                                .unwrap_or(\"\");\n                            bail!(\"Mattermost WebSocket closed during authentication: {reason}\");\n                        }","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/mattermost.rs#L426-L462","documentation":"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.","triggerScenarios":"`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.","commonSituations":"nginx/traefik without WebSocket upgrade configuration; load balancer with very short timeouts for new connections; Mattermost restarting mid-handshake; heavily loaded server.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let mut backoff = std::time::Duration::from_secs(1);\nloop {\n    match mm_channel.listen(tx.clone()).await {\n        Ok(()) => break Ok(()),\n        Err(e) if e.to_string().contains(\"authentication handshake timed out\") => {\n            tokio::time::sleep(backoff).await;\n            backoff = (backoff * 2).min(std::time::Duration::from_secs(60));\n        }\n        Err(e) => break Err(e),\n    }\n}","preventionTips":["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"],"tags":["mattermost","websocket","handshake","timeout","rust"],"backgroundTag":"websocket-handshake-timeout","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}