{"record":{"id":"8232b2693c700cc8","repo":"netbirdio/netbird","slug":"websocket-connection-timeout","errorCode":null,"errorMessage":"WebSocket connection timeout","messagePattern":"WebSocket connection timeout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/wsproxy/constants.go","lineNumber":17,"sourceCode":"package wsproxy\n\nimport \"errors\"\n\n// ProxyPath is the base path where the WebSocket proxy is mounted on servers.\nconst ProxyPath = \"/ws-proxy\"\n\n// Component paths that are appended to ProxyPath\nconst (\n\tManagementComponent = \"/management\"\n\tSignalComponent     = \"/signal\"\n\tFlowComponent       = \"/flow\"\n)\n\n// Common errors\nvar (\n\tErrConnectionTimeout  = errors.New(\"WebSocket connection timeout\")\n\tErrConnectionFailed   = errors.New(\"WebSocket connection failed\")\n\tErrBackendUnavailable = errors.New(\"backend unavailable\")\n)\n","sourceCodeStart":1,"sourceCodeEnd":21,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/util/wsproxy/constants.go#L1-L21","documentation":"Sentinel error from the browser/WASM gRPC dialer (util/wsproxy/client/dialer_js.go). WithWebSocketDialer opens a JavaScript WebSocket to ws(s)://<host>/ws-proxy/<component> and waits for the onopen event; if it does not fire within the 30-second dialTimeout, the socket is closed and this error is returned to the gRPC dialer.","triggerScenarios":"WASM client dialing a host that is unreachable or not serving WebSocket; the /ws-proxy/management|signal|flow route missing on the server; a TLS handshake that stalls (invalid cert, wrong scheme) for over 30 seconds.","commonSituations":"Reverse proxy in front of NetBird not forwarding the WebSocket upgrade; browser rejecting a self-signed certificate during wss; pointing the client at a plain-HTTP port with the wss scheme.","solutions":["Verify the ws-proxy path and component suffix exist and the proxy upgrades WebSocket connections.","Check TLS validity: the browser must trust the certificate for wss URLs.","If the handshake is legitimately slow, raise the dialTimeout constant in dialer_js.go and rebuild the WASM client."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isWsDialTimeout(err error) bool {\n    return errors.Is(err, wsproxy.ErrConnectionTimeout)\n}","tryCatchPattern":"conn, err := dialer(ctx)\nif err != nil {\n    if errors.Is(err, wsproxy.ErrConnectionTimeout) {\n        // handshake exceeded 30s: retry with backoff, then surface\n        // a user-actionable message (check proxy/TLS) after N attempts\n    }\n    return err\n}","preventionTips":["Verify the /ws-proxy/<component> route and WebSocket upgrade in the reverse proxy before shipping.","Ensure the browser trusts the certificate for wss URLs.","Keep an eye on the 30s dialTimeout constant if deploying over high-latency links."],"tags":["websocket","wasm","grpc","network","browser","timeout"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}