{"record":{"id":"3585aa8e4a29b1ee","repo":"chenhg5/cc-connect","slug":"read-w","errorCode":null,"errorMessage":"read: %w","messagePattern":"read: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket.go","lineNumber":271,"sourceCode":"\tslog.Info(\"wecom-ws: subscribed successfully\", \"bot_id\", p.botID)\n\tp.missedPong.Store(0)\n\n\t// Start heartbeat goroutine\n\theartCtx, heartCancel := context.WithCancel(p.ctx)\n\tdefer heartCancel()\n\tgo p.heartbeat(heartCtx, conn)\n\n\t// Read loop\n\tfor {\n\t\tselect {\n\t\tcase <-p.ctx.Done():\n\t\t\treturn p.ctx.Err()\n\t\tdefault:\n\t\t}\n\n\t\t_, raw, err := conn.ReadMessage()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"read: %w\", err)\n\t\t}\n\n\t\tvar frame wsFrame\n\t\tif err := json.Unmarshal(raw, &frame); err != nil {\n\t\t\tslog.Warn(\"wecom-ws: invalid json\", \"error\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tp.handleFrame(frame)\n\t}\n}\n\n// handleFrame dispatches incoming frames by cmd or req_id prefix.\nfunc (p *WSPlatform) handleFrame(frame wsFrame) {\n\tswitch frame.Cmd {\n\tcase \"aibot_msg_callback\":\n\t\tp.handleMsgCallback(frame)\n\tcase \"aibot_event_callback\":","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket.go#L253-L289","documentation":"In the steady-state message loop, conn.ReadMessage failed while reading the next WebSocket frame; the error is wrapped as 'read: %w'. This terminates runConnection for this attempt and connectLoop reconnects. Any successfully-read but malformed JSON frames are only logged and skipped — this error is specifically a transport-level read failure.","triggerScenarios":"Server closes the connection (close frame, EOF), network drops mid-frame, read deadline/pong timeout expires, or p.ctx cancellation interrupting the read.","commonSituations":"Idle connection reaped by a load balancer (mitigated by the heartbeat/pong mechanism); WeCom server maintenance restart; laptop sleep/network switch; proxy closing long-lived websocket connections.","solutions":["Confirm heartbeat/pong keepalive is functioning (missedPong resets) — frequent reads errors on idle links indicate keepalive issues.","Check the wrapped cause: 'close 1006' = abnormal close (network); clean close codes point to server-initiated disconnect.","Rely on connectLoop automatic reconnection; alert only on persistent failure.","Investigate NAT/load-balancer idle timeouts if drops occur after long silence."],"exampleFix":"null","handlingStrategy":"retry","validationCode":"if time.Since(p.lastPong) > pongTimeout { forceReconnect() } // detect half-open connections before reads fail","typeGuard":"null","tryCatchPattern":"err := platform.Start(ctx)\nif err != nil && strings.Contains(err.Error(), \"read:\") {\n\t// transport drop; connectLoop reconnects — only alert if persistent\n\tif consecutiveFailures > threshold { alert() }\n}","preventionTips":["Keep websocket heartbeat/pong keepalive enabled and tuned below NAT idle timeouts","Avoid routing long-lived wss through aggressive proxies","Distinguish clean server close codes from abnormal 1006 drops","Alert on reconnect loops, not single read errors"],"tags":["wecom","websocket","read","disconnect"],"backgroundTag":"websocket-connection-closed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}