chenhg5/cc-connect · error
weixin: missing context_token for peer %q - user must send a
Error message
weixin: missing context_token for peer %q - user must send a message to the bot first
What it means
WeChat's ilink sendmessage API requires a context_token captured from the user's most recent inbound message. If the reply context has no context_token and none is cached for the peer user, sendChunks refuses to send and returns this error, since any send attempt would be rejected by the server.
Source
Thrown at platform/weixin/weixin.go:820
}
func (p *Platform) sendChunks(ctx context.Context, replyCtx any, content string, path sendPath) error {
rc, ok := replyCtx.(*replyContext)
if !ok || rc == nil {
return fmt.Errorf("weixin: invalid reply context")
}
if err := p.checkSendQuota(ctx, path); err != nil {
return err
}
if strings.TrimSpace(rc.contextToken) == "" {
rc.contextToken = p.getContextToken(rc.peerUserID)
}
if strings.TrimSpace(rc.contextToken) == "" {
slog.Error("weixin: cannot send message - missing context_token",
"peer", rc.peerUserID,
"content_preview", truncatePreview(content, 100),
"hint", "user needs to send a message to the bot first so a context_token can be captured")
return fmt.Errorf("weixin: missing context_token for peer %q - user must send a message to the bot first", rc.peerUserID)
}
if strings.TrimSpace(content) == "" {
return nil
}
chunks := splitUTF8(content, maxWeixinChunk)
total := len(chunks)
for i, chunk := range chunks {
// Add delay between chunks to avoid rate limiting (except for first chunk)
if i > 0 {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(weixinChunkSendDelay):
}
}
err := p.sendChunk(ctx, rc, chunk)
if err != nil {
slog.Error("weixin: chunk send failed, message incomplete",View on GitHub (pinned to 4000b2338a)
Solutions
- Ask the user to send any message to the bot first, then reply within the token validity window
- Persist state_dir so context tokens survive restarts
- Convert proactive pushes into replies triggered by user activity
- Reduce push volume so tokens don't expire between sends
Defensive patterns
Strategy: fallback
Validate before calling
if strings.TrimSpace(rc.ContextToken) == "" {
// cannot push; queue message until the user messages the bot
queueForLaterReply(peerUserID, content)
return nil
} Try / catch
if err := p.Send(ctx, rc, msg); err != nil && strings.Contains(err.Error(), "missing context_token") {
log.Warn("user must message the bot first; queuing", "peer", rc.PeerUserID)
queueForLaterReply(rc.PeerUserID, msg)
} Prevention
- Never push to users who haven't messaged the bot
- Persist state_dir so captured tokens survive restarts
- Reply promptly while the user's token is fresh
- Treat pushes to silent peers as best-effort with fallback queuing
When it happens
Trigger: The bot attempts a proactive push (Send, cron, timer) to a peer who has never messaged the bot, or whose captured context_token is missing/expired from the token cache and sync buffer.
Common situations: Bot restarted with an empty state_dir losing cached tokens; user's context_token expired after inactivity; first-ever message to a new user attempted as a push instead of waiting for an inbound message.
Related errors
- antigravity: unknown permission request %q
- session is closed
- weixin: context_token is required for send
- weixin: missing context_token for peer %q
- weixin: convert %s to AMR: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/4cfaecea348cbf4b.
Report an issue: GitHub.