chenhg5/cc-connect · error

weixin: context_token is required for send

Error message

weixin: context_token is required for send

What it means

WeChat iLink outbound sends require a context_token obtained from the conversation config; the client enforces this before building the request. sendText refuses to send when the token is empty because the API would reject the message anyway.

Source

Thrown at platform/weixin/client.go:255

func (c *apiClient) sendTyping(ctx context.Context, userID, typingTicket string, status int) error {
	req := sendTypingReq{
		IlinkUserID:  userID,
		TypingTicket: typingTicket,
		Status:       status,
		BaseInfo:     baseInfo{ChannelVersion: channelVersion},
	}
	payload, err := json.Marshal(req)
	if err != nil {
		return err
	}
	_, err = c.post(ctx, "ilink/bot/sendtyping", payload, 0, "sendTyping")
	return err
}

func (c *apiClient) sendText(ctx context.Context, to, text, contextToken, clientID string) error {
	if strings.TrimSpace(contextToken) == "" {
		return fmt.Errorf("weixin: context_token is required for send")
	}
	items := []messageItem{}
	if strings.TrimSpace(text) != "" {
		items = append(items, messageItem{
			Type:     messageItemText,
			TextItem: &textItem{Text: text},
		})
	}
	if len(items) == 0 {
		return fmt.Errorf("weixin: sendText: empty item_list")
	}
	msg := sendMessageReq{
		Msg: weixinOutboundMsg{
			FromUserID:   "",
			ToUserID:     to,
			ClientID:     clientID,
			MessageType:  messageTypeBot,
			MessageState: messageStateFinish,

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Ensure getConfig runs and its result is cached per user before any send
  2. Fail earlier: propagate the getConfig error instead of continuing with an empty token
  3. Re-fetch config on this error and retry the send once
  4. Verify session persistence so tokens survive restarts

Example fix

// before
if err := getConfig(...); err != nil { slog.Warn("..."); }
c.sendText(ctx, to, text, token, id) // token may be empty
// after
if err := getConfig(...); err != nil { return fmt.Errorf("weixin: get config: %w", err) }
c.sendText(ctx, to, text, token, id)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(contextToken) == "" { return errors.New("cannot send: context_token missing; fetch config first") }

Type guard

func hasContextToken(t string) bool { return strings.TrimSpace(t) != "" }

Try / catch

if err := sendText(ctx, to, text, token, id); err != nil {
	if strings.Contains(err.Error(), "context_token is required") {
		if tok, cerr := fetchContextToken(ctx, to); cerr == nil { return sendText(ctx, to, text, tok, id) }
	}
	return err
}

Prevention

When it happens

Trigger: sendText invoked with an empty contextToken — typically when the getConfig step was skipped, failed silently, or the token expired and was cleared before this send.

Common situations: Bot restart losing cached per-user context tokens; getConfig previously failed (errors 1716/1717) and the caller proceeded anyway; first message to a user before config was ever fetched.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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