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

  1. Confirm the reconnect loop treats this as transient and redials (expected behavior)
  2. If drops are frequent, verify keep-alive/ping handling and any proxy idle timeouts
  3. Check host network stability (NAT timeout on idle WSS, VPN drops)
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/73606bbcf94b51aa. Report an issue: GitHub.