{"record":{"id":"ea30641ee5cd04d6","repo":"chenhg5/cc-connect","slug":"dial-s","errorCode":null,"errorMessage":"dial: %s","messagePattern":"dial: %s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"platform/tuitui/tuitui.go","lineNumber":329,"sourceCode":"\t\t}\n\t\tslog.Warn(\"tuitui: websocket disconnected, retrying\", \"error\", err, \"backoff\", backoff)\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase <-time.After(backoff):\n\t\t}\n\t\tbackoff *= 2\n\t\tif backoff > maxReconnectBackoff {\n\t\t\tbackoff = maxReconnectBackoff\n\t\t}\n\t}\n}\n\nfunc (p *Platform) runWS(ctx context.Context) error {\n\twsURL := p.wsBase + \"/robot/callback/ws?auth=\" + url.QueryEscape(p.appID+\".\"+p.appSecret)\n\tconn, _, err := websocket.DefaultDialer.DialContext(ctx, wsURL, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dial: %s\", core.RedactToken(err.Error(), p.appSecret))\n\t}\n\tdefer func() { _ = conn.Close() }()\n\tslog.Info(\"tuitui: websocket connected\")\n\n\tdone := make(chan error, 1)\n\tgo func() {\n\t\tfor {\n\t\t\t_, data, err := conn.ReadMessage()\n\t\t\tif err != nil {\n\t\t\t\tdone <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tp.handleFrame(ctx, conn, data)\n\t\t}\n\t}()\n\n\tselect {\n\tcase <-ctx.Done():","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/tuitui/tuitui.go#L311-L347","documentation":"runWS dials the TuiTui websocket endpoint /robot/callback/ws with appID.appSecret auth; if the websocket handshake fails, the dialer error is wrapped as \"dial: %s\" after passing it through core.RedactToken to scrub the app secret from the message. This is the low-level connection error surfaced by connectLoop's reconnect cycle.","triggerScenarios":"websocket.DefaultDialer.DialContext fails: server unreachable, DNS failure, TLS error, non-101 HTTP response (bad path/auth), or context cancellation mid-dial.","commonSituations":"TuiTui service outage or maintenance; corporate proxy/firewall blocking websocket upgrades; wrong wsBase URL configured; invalid appID/appSecret causing the server to reject the upgrade; network flaps triggering repeated reconnect dials.","solutions":["Check network connectivity and whether the TuiTui endpoint is reachable (the redacted message will indicate DNS vs TLS vs HTTP failure).","Verify wsBase and appID/appSecret in config — the auth query parameter embeds appID.appSecret and wrong values are rejected at handshake.","Rely on connectLoop's retry/backoff for transient outages; investigate persistent failures in platform status or proxy settings."],"exampleFix":"// before\nwsBase = \"https://wrong-host.example.com\" // dial fails\n// after\nwsBase = \"https://open.tuitui.example.com\" // correct endpoint from platform docs","handlingStrategy":"retry","validationCode":"// pre-check endpoint reachability\nconn, err := net.DialTimeout(\"tcp\", host, 5*time.Second)\nif err != nil {\n    log.Warn(\"tuitui endpoint unreachable\", \"err\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := p.Start(handler); err != nil {\n    log.Error(\"tuitui start failed\", \"err\", err) // dial errors surface via connectLoop logs\n    // rely on connectLoop backoff; alert after N consecutive failures\n}","preventionTips":["Monitor connectivity to the TuiTui websocket endpoint from your deployment.","Keep appID/appSecret valid — handshake auth embeds them.","Configure proxies/firewalls to allow websocket upgrades.","Use connectLoop's backoff and alert on sustained reconnect failure instead of crashing."],"tags":["websocket","network","connection"],"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"}