chenhg5/cc-connect · warning
read websocket: %w
Error message
read websocket: %w
What it means
The WebSocket read loop returns 'read websocket: %w' when conn.ReadMessage fails while the context is still alive (i.e., not a deliberate shutdown). This indicates the Mercury connection dropped unexpectedly; the outer loop will reconnect.
Source
Thrown at platform/webex/webex.go:346
}
connClosed := make(chan struct{})
defer close(connClosed)
go func() {
select {
case <-ctx.Done():
_ = conn.Close()
case <-connClosed:
}
}()
for {
_, data, err := conn.ReadMessage()
if err != nil {
if ctx.Err() != nil {
return nil
}
return fmt.Errorf("read websocket: %w", err)
}
p.handleFrame(ctx, data)
}
}
// handleFrame parses one Mercury WebSocket frame and dispatches qualifying
// messages. The frame body is encrypted, so we fetch the decrypted message
// via REST using the activity ID.
func (p *Platform) handleFrame(ctx context.Context, data []byte) {
var ev wsEvent
if err := json.Unmarshal(data, &ev); err != nil {
slog.Debug("webex: non-JSON frame", "error", err)
return
}
// "post" = text message, "share" = file/image upload. Other verbs (e.g.
// "update" for malware-scan completion, "delete") are re-notifications we
// must ignore to avoid double-processing.
if ev.Data.EventType != "conversation.activity" {View on GitHub (pinned to 4000b2338a)
Solutions
- Confirm the reconnect loop treats this as transient and redials (expected behavior)
- If drops are frequent, verify keep-alive/ping handling and any proxy idle timeouts
- Check host network stability (NAT timeout on idle WSS, VPN drops)
- Run with debug logs to capture the wrapped net.OpError / close message to distinguish server close vs. transport drop
Example fix
// before
if err := runConnection(ctx); err != nil {
slog.Error("fatal", "err", err) // treat as fatal
os.Exit(1)
}
// after
if err := runConnection(ctx); err != nil {
if ctx.Err() == nil {
slog.Warn("webex: websocket dropped, reconnecting", "err", err)
}
continue // reconnect
} Defensive patterns
Strategy: retry
Validate before calling
// ensure a supervised reconnect loop exists around the read loop // and that idle keep-alives (ping) are enabled
Try / catch
for {
_, data, err := conn.ReadMessage()
if err != nil {
if ctx.Err() != nil { return nil }
slog.Warn("ws dropped, reconnecting", "err", err)
return reconnectNeeded{err}
}
handleFrame(ctx, data)
} Prevention
- Always treat read errors as transient unless the context is cancelled
- Enable/verify WebSocket ping-pong keep-alives to survive NAT idle timeouts
- Deploy with a supervisor loop that redials after drops
- Log the wrapped error type to spot recurring infrastructure causes
When it happens
Trigger: Network interruption, server-side close/timeout, ping-pong timeout, or proxy idle-connection teardown causing ReadMessage to return an error while ctx is not cancelled.
Common situations: Long idle periods with intermediary (NAT/firewall) dropping the connection; Webex server maintenance; mobile/unstable network on the host.
Related errors
- cloud_web: websocket disconnected
- dial websocket: %s
- wecom-ws: ack timeout
- read register_ack: %w
- qq: ws connect failed (%s): %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/73606bbcf94b51aa.
Report an issue: GitHub.