{"record":{"id":"7164f3d46e15f373","repo":"chenhg5/cc-connect","slug":"errcode-d-errmsg-s","errorCode":null,"errorMessage":"errcode=%d errmsg=%s","messagePattern":"errcode=(.+?) errmsg=(.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/tuitui/tuitui.go","lineNumber":692,"sourceCode":"\t\t}\n\t} else if isImage {\n\t\tpayload[\"msgtype\"] = \"image\"\n\t\tpayload[\"image\"] = map[string]string{\"media_id\": mediaID}\n\t} else {\n\t\tpayload[\"msgtype\"] = \"attachment\"\n\t\tpayload[\"attachment\"] = map[string]string{\"media_id\": mediaID}\n\t}\n\taddTargets(payload, rctx.chatID, rctx.chatType)\n\treturn p.sendPayload(ctx, payload)\n}\n\nfunc (p *Platform) sendPayload(ctx context.Context, payload map[string]any) error {\n\tvar response sendMessageResponse\n\tif err := p.postJSON(ctx, \"/robot/message/custom/send\", payload, &response); err != nil {\n\t\treturn err\n\t}\n\tif response.ErrCode != 0 {\n\t\treturn fmt.Errorf(\"errcode=%d errmsg=%s\", response.ErrCode, response.ErrMsg)\n\t}\n\tp.rememberOutboundEcho(response.messageIDs())\n\treturn nil\n}\n\nfunc (p *Platform) postJSON(ctx context.Context, apiPath string, payload any, out any) error {\n\tdata, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn err\n\t}\n\tu, err := url.Parse(p.apiBase + apiPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq := u.Query()\n\tq.Set(\"appid\", p.appID)\n\tq.Set(\"secret\", p.appSecret)\n\tu.RawQuery = q.Encode()","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/tuitui/tuitui.go#L674-L710","documentation":"sendPayload posts to /robot/message/custom/send and checks the API's application-level status. Tuitui returns HTTP 200 with errcode/errmsg in the body; a non-zero errcode means the API rejected the message (auth, permission, bad recipient, rate limit). The adapter surfaces both fields verbatim.","triggerScenarios":"Sending a text/media message where the Tuitui backend answers errcode != 0 — e.g. invalid webhook key, bot not in the chat, message blocked, or rate limited.","commonSituations":"Rotated or wrong app secret/webhook URL; bot removed from group; message content violating platform rules; exceeding send rate limits.","solutions":["Read errmsg in the error: fix the specific API complaint (credentials, recipient ID, content).","Verify the webhook URL and app secret in config.toml are current.","Confirm the bot is still a member of the target chat and has send permission.","If errcode indicates rate limiting, add backoff/retry before resending."],"exampleFix":"// before\nerr := p.sendPayload(ctx, payload) // errcode=300005, msg ignored\n// after\nif err != nil {\n    slog.Error(\"tuitui: send failed\", \"err\", err) // shows errcode + errmsg\n    if isRateLimitErrcode(err) { time.Sleep(backoff) }\n}","handlingStrategy":"retry","validationCode":"if rctx.chatID == \"\" {\n    return errors.New(\"cannot send: empty chatID\")\n}","typeGuard":null,"tryCatchPattern":"if err := p.sendPayload(ctx, payload); err != nil {\n    var apiErr errcodeError\n    if errors.As(err, &apiErr) && apiErr.Retryable() {\n        time.Sleep(backoff); return p.sendPayload(ctx, payload)\n    }\n    return fmt.Errorf(\"tuitui send: %w\", err)\n}","preventionTips":["Keep app_secret and webhook URL current in config.toml","Confirm bot membership/permissions before sending to a chat","Respect rate limits; back off on limit errcodes"],"tags":["api","tuitui","http"],"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"}