{"record":{"id":"c5b907f45331b713","repo":"chenhg5/cc-connect","slug":"dial-websocket-s","errorCode":null,"errorMessage":"dial websocket: %s","messagePattern":"dial websocket: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/webex/webex.go","lineNumber":321,"sourceCode":"func (p *Platform) runConnection(ctx context.Context) error {\n\tdev, err := p.client.CreateDevice(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create device: %w\", err)\n\t}\n\tp.mu.Lock()\n\tprevDevice := p.deviceURL\n\tp.deviceURL = dev.URL\n\tp.mu.Unlock()\n\tif prevDevice != \"\" && prevDevice != dev.URL {\n\t\tif err := p.client.DeleteDevice(ctx, prevDevice); err != nil {\n\t\t\tslog.Debug(\"webex: delete stale device failed\", \"error\", err)\n\t\t}\n\t}\n\n\theader := map[string][]string{\"Authorization\": {\"Bearer \" + p.token}}\n\tconn, _, err := websocket.DefaultDialer.DialContext(ctx, dev.WebSocketURL, header)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dial websocket: %s\", core.RedactToken(err.Error(), p.token))\n\t}\n\tdefer func() { _ = conn.Close() }()\n\n\tslog.Info(\"webex: websocket connected\")\n\tif h := p.lifecycle(); h != nil {\n\t\th.OnPlatformReady(p)\n\t}\n\n\tconnClosed := make(chan struct{})\n\tdefer close(connClosed)\n\tgo func() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\t_ = conn.Close()\n\t\tcase <-connClosed:\n\t\t}\n\t}()\n","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/webex/webex.go#L303-L339","documentation":"After device registration, runConnection dials the Mercury WebSocket URL. A failed DialContext (with the bot's Bearer token in headers) yields 'dial websocket: %s', with the bot token redacted from the error text. The real-time event stream could not be established.","triggerScenarios":"DialContext fails: network unreachable, TLS error, non-101 upgrade response, invalid/expired token rejected at upgrade, or DNS failure for the Mercury host.","commonSituations":"Corporate firewalls/proxies blocking outbound WSS; token revoked while running; transient network drop during reconnect; expired certificate or MITM proxy interfering with TLS.","solutions":["Confirm outbound WSS access to the Mercury host from the deployment environment (firewall/proxy)","Re-validate the bot token — 401 at upgrade means the token is invalid/expired","Inspect the redacted error text for the exact transport failure (x509, no route, handshake)","Check proxy environment variables (HTTPS_PROXY) are set correctly if behind a corporate proxy"],"exampleFix":"// before\n// running in container without egress to wss://\n// after\n// open firewall: outbound TCP 443 to webexapis.com and Mercury WSS hosts\n// or configure HTTPS_PROXY for the daemon environment","handlingStrategy":"retry","validationCode":"host := \"mercury-connection.webexapis.com\"\nconn, err := net.DialTimeout(\"tcp\", host+\":443\", 5*time.Second)\nif err != nil { log.Fatal(\"no egress to webex websocket\") }","typeGuard":null,"tryCatchPattern":"conn, _, err := dialer.DialContext(ctx, wsURL, header)\nif err != nil {\n    if ctx.Err() == nil { slog.Warn(\"ws dial failed, retrying\", \"err\", err) }\n    return retryable{err}\n}","preventionTips":["Verify outbound TCP 443 / WSS egress in firewalled and containerized environments before deployment","Set HTTPS_PROXY correctly behind corporate proxies","Re-validate tokens on upgrade failures (401 at handshake)","Keep TLS roots updated in minimal container images"],"tags":["webex","websocket","network","tls"],"backgroundTag":"connection-refused","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}