{"record":{"id":"332929c4f5375468","repo":"chenhg5/cc-connect","slug":"weixin-invalid-reply-context","errorCode":null,"errorMessage":"weixin: invalid reply context","messagePattern":"weixin: invalid reply context","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/weixin/media_outbound.go","lineNumber":44,"sourceCode":"\tu, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := strings.ToLower(u.Hostname())\n\treturn strings.HasSuffix(host, \".weixin.qq.com\") || strings.HasSuffix(host, \".wechat.com\")\n}\n\ntype cdnUploadedRef struct {\n\tdownloadParam string\n\taesKey        []byte\n\tcipherSize    int\n\trawSize       int\n}\n\nfunc (p *Platform) resolveReplyContext(replyCtx any) (*replyContext, error) {\n\trc, ok := replyCtx.(*replyContext)\n\tif !ok || rc == nil {\n\t\treturn nil, fmt.Errorf(\"weixin: invalid reply context\")\n\t}\n\tif strings.TrimSpace(rc.contextToken) == \"\" {\n\t\trc.contextToken = p.getContextToken(rc.peerUserID)\n\t}\n\tif strings.TrimSpace(rc.contextToken) == \"\" {\n\t\treturn nil, fmt.Errorf(\"weixin: missing context_token for peer %q\", rc.peerUserID)\n\t}\n\treturn rc, nil\n}\n\nfunc (p *Platform) uploadToWeixinCDN(ctx context.Context, to string, plaintext []byte, mediaType int, label string) (*cdnUploadedRef, error) {\n\tif len(plaintext) == 0 {\n\t\treturn nil, fmt.Errorf(\"weixin: %s: empty payload\", label)\n\t}\n\tif strings.TrimSpace(p.cdnBaseURL) == \"\" {\n\t\treturn nil, fmt.Errorf(\"weixin: cdn_base_url is empty\")\n\t}\n\trawSize := len(plaintext)","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/weixin/media_outbound.go#L26-L62","documentation":"resolveReplyContext rejects any replyCtx that is not a non-nil *replyContext pointer. SendImage, SendFile and SendAudio all funnel through this function, so passing any other type (or nil) for the reply context makes media sending fail immediately with this message.","triggerScenarios":"Calling SendImage/SendFile/SendAudio with replyCtx set to nil, to a different concrete type (e.g. *otherPlatform.ReplyContext, string, struct value), or a typed-nil pointer of another type.","commonSituations":"Code copied from another platform adapter that passes its own reply-context type; callers that store the context as an interface{} and lose the concrete *replyContext type; passing a value (non-pointer) replyContext.","solutions":["Pass the *replyContext value that was originally returned/provided by the weixin platform when the inbound message was received.","Check that you are not passing a value type or a different platform's context type; only *weixin.replyContext is accepted.","Ensure the replyCtx is non-nil before calling; nil triggers this same error."],"exampleFix":"// before\nerr := p.SendImage(ctx, someStringCtx, img) // weixin: invalid reply context\n// after\nrc := &replyContext{peerUserID: peer, contextToken: token}\nerr := p.SendImage(ctx, rc, img)","handlingStrategy":"type-guard","validationCode":"if rc, ok := replyCtx.(*weixinReplyContext); !ok || rc == nil { return errors.New(\"no valid weixin reply context\") }","typeGuard":"func isWeixinReplyContext(v any) bool { rc, ok := v.(*replyContext); return ok && rc != nil }","tryCatchPattern":"if err := p.SendImage(ctx, rc, img); err != nil && strings.Contains(err.Error(), \"invalid reply context\") { log.Error(\"wrong replyCtx type passed to weixin SendImage\") }","preventionTips":["Always pass the reply context object handed to you by the weixin platform inbound path","Never store reply contexts as bare strings or untyped values","Add a unit test asserting the concrete type passed to Send*"],"tags":["weixin","media","type-mismatch","reply-context"],"backgroundTag":"type-mismatch","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}