{"record":{"id":"a297f165638a3c85","repo":"chenhg5/cc-connect","slug":"wecom-ws-chatid-is-empty-cannot-send-file","errorCode":null,"errorMessage":"wecom-ws: chatID is empty, cannot send file","messagePattern":"wecom-ws: chatID is empty, cannot send file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket_outbound_media.go","lineNumber":165,"sourceCode":"\tcase \"image/jpeg\", \"image/jpg\":\n\t\treturn \"image.jpg\"\n\tcase \"image/gif\":\n\t\treturn \"image.gif\"\n\tcase \"image/webp\":\n\t\treturn \"image.webp\"\n\tdefault:\n\t\treturn \"image.png\"\n\t}\n}\n\n// SendFile uploads and sends a file through the WeCom AI Bot WebSocket API.\nfunc (p *WSPlatform) SendFile(ctx context.Context, rctx any, file core.FileAttachment) error {\n\trc, ok := rctx.(wsReplyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"wecom-ws: SendFile: invalid reply context type %T\", rctx)\n\t}\n\tif rc.chatID == \"\" {\n\t\treturn fmt.Errorf(\"wecom-ws: chatID is empty, cannot send file\")\n\t}\n\tif len(file.Data) == 0 {\n\t\treturn fmt.Errorf(\"wecom-ws: file data is empty\")\n\t}\n\n\tmediaID, err := p.uploadWSMedia(ctx, \"file\", wsFileFileName(file), file.Data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"wecom-ws: send file: %w\", err)\n\t}\n\tif err := p.sendWSMediaMessage(ctx, rc.chatID, \"file\", mediaID); err != nil {\n\t\treturn fmt.Errorf(\"wecom-ws: send file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc wsFileFileName(file core.FileAttachment) string {\n\tname := filepath.Base(strings.TrimSpace(file.FileName))\n\tif name != \"\" && name != \".\" {","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket_outbound_media.go#L147-L183","documentation":"SendFile found a valid wsReplyContext but its chatID field is empty, so there is no WeCom chat to deliver the file to. The platform refuses the call rather than sending a message that the API would reject.","triggerScenarios":"Called with a wsReplyContext that was constructed with an empty chatID — e.g. a zero-value wsReplyContext{}, or an inbound event that lacked chat identity.","commonSituations":"Reply context created programmatically in tests or cron/scheduled jobs with no inbound chat; webhook event parsed but chat_id field missing from payload; context default-initialized instead of populated from the message.","solutions":["Populate rc.chatID from the inbound WeCom message's chat id before calling SendFile","Guard callers: skip or queue file sends when no chatID is known","In scheduled/bot-initiated flows, resolve a target chatID from configuration instead of relying on reply context"],"exampleFix":"// before\nvar rc wsReplyContext // chatID empty\np.SendFile(ctx, rc, file)\n// after\nrc := wsReplyContext{chatID: inboundMsg.ChatID}\nif rc.chatID == \"\" {\n\treturn fmt.Errorf(\"no target chat for file\")\n}\np.SendFile(ctx, rc, file)","handlingStrategy":"validation","validationCode":"if rc, ok := rctx.(wsReplyContext); !ok || rc.chatID == \"\" {\n\treturn fmt.Errorf(\"cannot send file: no wecom chat id\")\n}","typeGuard":"func hasChatID(rctx any) bool {\n\trc, ok := rctx.(wsReplyContext)\n\treturn ok && rc.chatID != \"\"\n}","tryCatchPattern":"if err := p.SendFile(ctx, rc, file); err != nil && strings.Contains(err.Error(), \"chatID is empty\") {\n\tlog.Warn(\"skipping file send: no target chat\")\n}","preventionTips":["Always populate chatID from the inbound event before replying","In cron/scheduled flows resolve a chatID from config instead of reply context","Assert chatID non-empty in tests that build reply contexts"],"tags":["validation","empty-field","wecom"],"backgroundTag":"empty-required-field","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"}