{"record":{"id":"faebf40ffbfe4033","repo":"sipeed/picoclaw","slug":"weixin-send-media-missing-context-token-for-chat","errorCode":null,"errorMessage":"weixin send media: missing context token for chat %s: %w","messagePattern":"weixin send media: missing context token for chat (.+?): %w","errorType":"exception","errorClass":"channels.ErrSendFailed","httpStatus":null,"severity":"error","filePath":"pkg/channels/weixin/media.go","lineNumber":1113,"sourceCode":"\n\treturn stop, nil\n}\n\n// SendMedia implements channels.MediaSender.\nfunc (c *WeixinChannel) SendMedia(ctx context.Context, msg bus.OutboundMediaMessage) ([]string, error) {\n\tif !c.IsRunning() {\n\t\treturn nil, basechannels.ErrNotRunning\n\t}\n\tif err := c.ensureSessionActive(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tcontextToken := \"\"\n\tif v, ok := c.contextTokens.Load(msg.ChatID); ok {\n\t\tcontextToken, _ = v.(string)\n\t}\n\tif contextToken == \"\" {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"weixin send media: missing context token for chat %s: %w\",\n\t\t\tmsg.ChatID,\n\t\t\tbasechannels.ErrSendFailed,\n\t\t)\n\t}\n\n\tfor _, part := range msg.Parts {\n\t\tlocalPath, filename, contentType, cleanup, err := c.resolveOutboundPart(ctx, part)\n\t\tif err != nil {\n\t\t\tlogger.ErrorCF(\"weixin\", \"Failed to resolve outbound media\", map[string]any{\n\t\t\t\t\"chat_id\": msg.ChatID,\n\t\t\t\t\"ref\":     part.Ref,\n\t\t\t\t\"error\":   err.Error(),\n\t\t\t})\n\t\t\treturn nil, fmt.Errorf(\"weixin send media: %w\", basechannels.ErrSendFailed)\n\t\t}\n\t\tfunc() {\n\t\t\tif cleanup != nil {","sourceCodeStart":1095,"sourceCodeEnd":1131,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/weixin/media.go#L1095-L1131","documentation":"The media send path of the Weixin channel refuses to send because no context_token is stored for msg.ChatID — the same gating as error 674 but for media parts. The iLink media upload/send calls need the token captured from the user's last inbound message; without it the channel fails fast (wrapping basechannels.ErrSendFailed) before resolving or uploading any files.","triggerScenarios":"Sending media to a chat that has not messaged the bot since channel start; after a restart (tokens are in-memory, c.contextTokens is cleared); using a ChatID that differs from the key stored on inbound.","commonSituations":"Bot tries to proactively push an image/file to a user who never wrote; restart wiped tokens so media replies to old threads fail until the user pings again; media reply routed through a rewritten chat id.","solutions":["Ensure an inbound message from the chat exists before sending media (reply-only semantics)\nAfter restarts, have users send any message to re-arm the token, then resend the media\nVerify msg.ChatID equals the inbound user id exactly (no mapping drift)\nSend the payload as text/URL if the user cannot be asked to re-engage"],"exampleFix":"// before\nch.SendMedia(ctx, msg) // msg.ChatID never seen inbound -> error 677\n\n// after\nif _, ok := ch.ContextToken(msg.ChatID); !ok {\n    return skipMediaReply(msg.ChatID)\n}\nch.SendMedia(ctx, msg)","handlingStrategy":"validation","validationCode":"// gate media sends on token availability (export ContextToken)\nif _, ok := ch.ContextToken(msg.ChatID); !ok {\n    return skipMedia(msg.ChatID) // nothing to send without an inbound history\n}\nreturn ch.SendMedia(ctx, msg)","typeGuard":"func isWeixinMediaMissingToken(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"weixin send media: missing context token\")\n}","tryCatchPattern":"if _, err := ch.SendMedia(ctx, msg); err != nil {\n    if isWeixinMediaMissingToken(err) {\n        // non-retryable: stash the media; deliver on the user's next inbound\n        parkUntilInbound(msg)\n    }\n}","preventionTips":["Treat media replies like text replies: inbound-first, always","Re-check token presence after channel restarts before flushing queued media","Use the identical ChatID string captured from inbound for sends","Offer a text/URL fallback when media cannot legally be delivered"],"tags":["weixin","media","context-token","state","send-failed"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}