{"record":{"id":"6bd5c68fbf57cfc9","repo":"chenhg5/cc-connect","slug":"wecom-ws-ack-error-errcode-d-errmsg-s","errorCode":null,"errorMessage":"wecom-ws: ack error: errcode=%d errmsg=%s","messagePattern":"wecom-ws: ack error: errcode=(.+?) errmsg=(.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket.go","lineNumber":304,"sourceCode":"\tswitch frame.Cmd {\n\tcase \"aibot_msg_callback\":\n\t\tp.handleMsgCallback(frame)\n\tcase \"aibot_event_callback\":\n\t\tslog.Debug(\"wecom-ws: event callback received (ignored)\", \"req_id\", frame.Headers.ReqID)\n\tcase \"\":\n\t\t// Response frame (no cmd): identify by req_id prefix\n\t\treqID := frame.Headers.ReqID\n\t\tswitch {\n\t\tcase strings.HasPrefix(reqID, \"ping\"):\n\t\t\tp.missedPong.Store(0)\n\t\t\tslog.Debug(\"wecom-ws: heartbeat ack received\")\n\t\tcase strings.HasPrefix(reqID, \"aibot_subscribe\"):\n\t\t\t// Late subscribe ack (should have been consumed in runConnection)\n\t\t\tslog.Debug(\"wecom-ws: late subscribe ack\")\n\t\tdefault:\n\t\t\tvar ackErr error\n\t\t\tif frame.ErrCode != nil && *frame.ErrCode != 0 {\n\t\t\t\tackErr = fmt.Errorf(\"wecom-ws: ack error: errcode=%d errmsg=%s\", *frame.ErrCode, frame.ErrMsg)\n\t\t\t\tslog.Warn(\"wecom-ws: reply/send ack error\", \"req_id\", reqID, \"errcode\", *frame.ErrCode, \"errmsg\", frame.ErrMsg)\n\t\t\t} else {\n\t\t\t\tslog.Debug(\"wecom-ws: reply/send ack ok\", \"req_id\", reqID)\n\t\t\t}\n\t\t\tp.dispatchAck(reqID, wsAckResult{frame: frame, err: ackErr})\n\t\t}\n\tdefault:\n\t\tslog.Debug(\"wecom-ws: unhandled cmd\", \"cmd\", frame.Cmd)\n\t}\n}\n\nfunc (p *WSPlatform) dispatchAck(reqID string, result wsAckResult) {\n\tch, ok := p.pendingAcks.LoadAndDelete(reqID)\n\tif !ok {\n\t\treturn\n\t}\n\tresultCh, ok := ch.(chan wsAckResult)\n\tif !ok {","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket.go#L286-L322","documentation":"handleFrame dispatches ack frames for outstanding reply/send requests (matched by req_id). When the ack frame carries a non-zero errcode, this error is built with the server's errcode/errmsg and delivered to the waiting caller via dispatchAck. It means the specific message send/reply was rejected by WeCom at the application level — the connection and subscription are fine.","triggerScenarios":"A reply/send frame got an ack with errcode != 0 — e.g. invalid target chat/user ID, message content rejected, permission error, or expired req_id/session on the server side.","commonSituations":"Bot not authorized to message the target user (user never messaged the bot first); malformed markdown/text content rejected; sending to a chat the bot was removed from; content violating platform policies.","solutions":["Log/inspect errcode and look it up in WeCom's error code docs to identify the rejection reason.","Verify the target user/chat ID is valid and the bot is permitted to message it (user must have interacted with the bot first for 1:1 messages).","Check message content for prohibited or oversized payload rejected by the server.","Surface the error to the caller (dispatchAck already does) and add retry logic only for retryable errcodes."],"exampleFix":"// before\np.dispatchAck(reqID, wsAckResult{frame: frame, err: ackErr}) // caller ignores ackErr\n// after\nres := <-ackCh\nif res.err != nil {\n\tslog.Error(\"wecom send rejected\", \"req_id\", reqID, \"err\", res.err)\n\treturn res.err\n}","handlingStrategy":"try-catch","validationCode":"// before sending: ensure target is valid and has interacted with the bot\nif !botKnowsUser(personID) { return errors.New(\"user must message the bot first\") }","typeGuard":"null","tryCatchPattern":"res, err := p.SendAndWaitAck(ctx, msg)\nif res.err != nil {\n\tswitch {\n\tcase isRetryableAckCode(res.err): retryWithBackoff()\n\tdefault: return fmt.Errorf(\"wecom rejected send: %w\", res.err)\n\t}\n}","preventionTips":["Only message users who have initiated contact with the bot","Validate target chat/user IDs before sending","Keep message payloads within WeCom size/policy limits","Always check ack results rather than fire-and-forget sends"],"tags":["wecom","websocket","ack","api"],"backgroundTag":"api-error-response","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"}