{"record":{"id":"b11fa6d823abd389","repo":"chenhg5/cc-connect","slug":"wecom-ws-chatid-is-empty-cannot-send-image","errorCode":null,"errorMessage":"wecom-ws: chatID is empty, cannot send image","messagePattern":"wecom-ws: chatID is empty, cannot send image","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket_outbound_media.go","lineNumber":28,"sourceCode":"\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/chenhg5/cc-connect/core\"\n)\n\nconst (\n\twecomWSUploadChunkSize = 512 * 1024\n\twecomWSUploadMaxChunks = 100\n)\n\n// SendImage uploads and sends an image through the WeCom AI Bot WebSocket API.\nfunc (p *WSPlatform) SendImage(ctx context.Context, rctx any, img core.ImageAttachment) error {\n\trc, ok := rctx.(wsReplyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"wecom-ws: SendImage: invalid reply context type %T\", rctx)\n\t}\n\tif rc.chatID == \"\" {\n\t\treturn fmt.Errorf(\"wecom-ws: chatID is empty, cannot send image\")\n\t}\n\tif len(img.Data) == 0 {\n\t\treturn fmt.Errorf(\"wecom-ws: image data is empty\")\n\t}\n\n\tmediaID, err := p.uploadWSMedia(ctx, \"image\", wsImageFileName(img), img.Data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"wecom-ws: send image: %w\", err)\n\t}\n\tif err := p.sendWSMediaMessage(ctx, rc.chatID, \"image\", mediaID); err != nil {\n\t\treturn fmt.Errorf(\"wecom-ws: send image: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (p *WSPlatform) uploadWSMedia(ctx context.Context, mediaType, filename string, data []byte) (string, error) {\n\ttotalChunks := (len(data) + wecomWSUploadChunkSize - 1) / wecomWSUploadChunkSize\n\tif totalChunks == 0 {","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket_outbound_media.go#L10-L46","documentation":"After the reply context passes the type check, SendImage verifies that wsReplyContext.chatID is non-empty because the WeCom WebSocket media-send API requires a target chat id. This error means a correctly typed reply context was passed, but its chatID field was never populated. The platform refuses to send to an empty destination rather than issuing a doomed API call.","triggerScenarios":"Calling SendImage with a wsReplyContext whose chatID field is the empty string — e.g. a zero-value context, a context built from an inbound message that lacked a chat id, or a context explicitly cleared/reset before the send.","commonSituations":"Zero-value struct literals in tests (`wsReplyContext{}`); inbound WeCom messages of a type that does not carry a chatid and whose context was still used for media replies; race where a session/reply context is reset by /new or session switch before a queued image send executes.","solutions":["Populate chatID from the inbound message before replying; do not pass a zero-value wsReplyContext.","Guard the call site: skip media sends when the originating message has no chatid.","Check upstream message parsing — if chatid is missing from real inbound messages, fix the parsing layer rather than working around it.","In tests, construct the context with a non-empty chatID."],"exampleFix":"// before\nrc := wsReplyContext{} // chatID empty\nerr := platform.SendImage(ctx, rc, img)\n// after\nrc := wsReplyContext{chatID: msg.ChatID}\nif rc.chatID == \"\" { return nil } // skip or handle upstream\nerr := platform.SendImage(ctx, rc, img)","handlingStrategy":"validation","validationCode":"// caller-side, before invoking SendImage on a context you own\nif chatID == \"\" {\n    return errors.New(\"cannot send image: chat id is empty\")\n}","typeGuard":null,"tryCatchPattern":"if err := platform.SendImage(ctx, rctx, img); err != nil {\n    if strings.Contains(err.Error(), \"chatID is empty\") {\n        // drop the send or request the user re-trigger from a valid chat\n    }\n}","preventionTips":["Always populate chatID from the inbound message before building a reply context.","Skip media replies for inbound message types that carry no chatid.","Re-resolve the reply context after session resets (/new, /switch) before sending queued media.","Never construct zero-value reply contexts in tests or production code."],"tags":["go","wecom","empty-value","chatid"],"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"}