{"record":{"id":"eaf2fbfedb361eb1","repo":"chenhg5/cc-connect","slug":"qq-ws-write-w","errorCode":null,"errorMessage":"qq: ws write: %w","messagePattern":"qq: ws write: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qq/qq.go","lineNumber":561,"sourceCode":"\t}\n\tif params != nil {\n\t\treq[\"params\"] = params\n\t}\n\n\tch := make(chan json.RawMessage, 1)\n\tp.echoCh.Store(echo, ch)\n\tdefer p.echoCh.Delete(echo)\n\n\tdata, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tp.mu.Lock()\n\terr = p.conn.WriteMessage(websocket.TextMessage, data)\n\tp.mu.Unlock()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"qq: ws write: %w\", err)\n\t}\n\n\tselect {\n\tcase raw := <-ch:\n\t\tvar resp struct {\n\t\t\tStatus  string          `json:\"status\"`\n\t\t\tRetCode int             `json:\"retcode\"`\n\t\t\tData    json.RawMessage `json:\"data\"`\n\t\t}\n\t\tif json.Unmarshal(raw, &resp) != nil {\n\t\t\treturn nil, fmt.Errorf(\"qq: invalid API response\")\n\t\t}\n\t\tif resp.RetCode != 0 {\n\t\t\treturn nil, fmt.Errorf(\"qq: API %s failed (retcode=%d)\", action, resp.RetCode)\n\t\t}\n\t\tvar result map[string]any\n\t\t_ = json.Unmarshal(resp.Data, &result)\n\t\treturn result, nil","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qq/qq.go#L543-L579","documentation":"Returned by callAPI when writing the API request frame to the OneBot WebSocket connection fails (p.conn.WriteMessage under p.mu). callAPI is the shared request path for Start, Send, SendImage, and resolveGroupName, so any of these surface this error when the connection is dead. Typically indicates a closed or broken connection.","triggerScenarios":"Any callAPI invocation while the WebSocket is closed/dropped: server restarted, network blip, ping/pong keepalive missed, or connection never established; concurrent sends racing a reconnect can also hit a nil/stale conn.","commonSituations":"OneBot server restarted or reconnected while the adapter kept the old socket; laptop sleep/network switch; long-lived idle connection silently dropped by a NAT/firewall without the adapter noticing.","solutions":["Reconnect: restart the platform (Start) or implement automatic WS reconnect with backoff.","Check the wrapped error — 'broken pipe'/'use of closed network connection' confirms a dead socket.","Enable/verify OneBot heartbeat (ping/pong) so dead connections are detected early.","Ensure Start completed successfully before issuing API calls; a failed Start leaves conn nil.","Serialize sends through a healthy-connection check; recreate the socket on first write failure."],"exampleFix":"// before\nerr = p.conn.WriteMessage(websocket.TextMessage, data)\n\n// after\nif p.conn == nil {\n    return nil, fmt.Errorf(\"qq: not connected\")\n}\np.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))\nerr = p.conn.WriteMessage(websocket.TextMessage, data)","handlingStrategy":"retry","validationCode":"func (p *Platform) wsHealthy() bool {\n    p.mu.Lock()\n    defer p.mu.Unlock()\n    if p.conn == nil {\n        return false\n    }\n    return p.conn.WriteControl(websocket.PingMessage, nil, time.Now().Add(3*time.Second)) == nil\n}","typeGuard":null,"tryCatchPattern":"result, err := p.callAPI(\"send_group_msg\", params)\nif err != nil && strings.Contains(err.Error(), \"ws write\") {\n    if rerr := p.reconnect(ctx); rerr != nil {\n        return fmt.Errorf(\"qq: reconnect after ws write failure: %w\", rerr)\n    }\n    result, err = p.callAPI(\"send_group_msg\", params)\n}","preventionTips":["Implement WS auto-reconnect with exponential backoff","Enable ping/pong keepalive to detect dead sockets early","Set write deadlines on every frame","Check Start succeeded before issuing API calls","Monitor network stability in deployment (sleep, VPN, NAT timeouts)"],"tags":["websocket","network","broken-pipe","onebot"],"backgroundTag":"broken-pipe","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"}