chenhg5/cc-connect · error
weixin: missing context_token for peer %q
Error message
weixin: missing context_token for peer %q
What it means
After type-checking the reply context, resolveReplyContext tries to fill rc.contextToken from getContextToken(rc.peerUserID). If both the supplied token and the cached lookup are blank, media upload cannot be tied to a valid WeChat session, so it fails with this formatted message naming the peer.
Source
Thrown at platform/weixin/media_outbound.go:50
}
type cdnUploadedRef struct {
downloadParam string
aesKey []byte
cipherSize int
rawSize int
}
func (p *Platform) resolveReplyContext(replyCtx any) (*replyContext, error) {
rc, ok := replyCtx.(*replyContext)
if !ok || rc == nil {
return nil, fmt.Errorf("weixin: invalid reply context")
}
if strings.TrimSpace(rc.contextToken) == "" {
rc.contextToken = p.getContextToken(rc.peerUserID)
}
if strings.TrimSpace(rc.contextToken) == "" {
return nil, fmt.Errorf("weixin: missing context_token for peer %q", rc.peerUserID)
}
return rc, nil
}
func (p *Platform) uploadToWeixinCDN(ctx context.Context, to string, plaintext []byte, mediaType int, label string) (*cdnUploadedRef, error) {
if len(plaintext) == 0 {
return nil, fmt.Errorf("weixin: %s: empty payload", label)
}
if strings.TrimSpace(p.cdnBaseURL) == "" {
return nil, fmt.Errorf("weixin: cdn_base_url is empty")
}
rawSize := len(plaintext)
aesKey := make([]byte, 16)
if _, err := rand.Read(aesKey); err != nil {
return nil, fmt.Errorf("weixin: %s: aes key: %w", label, err)
}
filekey := randomHex(16)
req := getUploadURLRequest{View on GitHub (pinned to 4000b2338a)
Solutions
- Set contextToken explicitly in the replyContext from the most recent inbound message from that peer.
- Have the user send any message to the bot first so getContextToken can cache a valid token, then retry the media send.
- Persist context tokens across restarts so media sends after a restart still have a token.
Example fix
// before
rc := &replyContext{peerUserID: peer} // missing context_token
// after
rc := &replyContext{peerUserID: peer, contextToken: lastInboundToken(peer)} Defensive patterns
Strategy: validation
Validate before calling
if strings.TrimSpace(rc.contextToken) == "" && p.getContextToken(rc.peerUserID) == "" { return errors.New("cannot send media: no context token for peer") } Try / catch
if err := p.SendImage(ctx, rc, img); err != nil && strings.Contains(err.Error(), "missing context_token") { requestUserToMessageFirst(peer) } Prevention
- Capture context_token from every inbound message immediately
- Persist tokens to disk so restarts don't lose them
- Only send media to peers with a known recent inbound message
When it happens
Trigger: Sending an image/file/audio where the replyContext has an empty contextToken AND the platform has no stored context token for that peerUserID (e.g. token expired, never recorded, or in-memory cache lost after restart).
Common situations: Proactively sending media to a user without a prior inbound message to anchor the context_token; bot restarted losing cached tokens; peer context expired on WeChat's side.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- weixin: context_token is required for send
- weixin: invalid reply context
- weixin: %s: empty payload
- weixin: cdn_base_url is empty
- weixin: %s: aes key: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/986f9d39cc167731.
Report an issue: GitHub.