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

  1. Set contextToken explicitly in the replyContext from the most recent inbound message from that peer.
  2. Have the user send any message to the bot first so getContextToken can cache a valid token, then retry the media send.
  3. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/986f9d39cc167731. Report an issue: GitHub.