chenhg5/cc-connect · error

weixin: invalid reply context

Error message

weixin: invalid reply context

What it means

sendChunks expects replyCtx to be the platform-internal *replyContext produced by newReplyContext. If the caller passes nil or a value of any other type, the type assertion fails and this error is returned — it signals an internal API misuse, not a user-facing condition.

Source

Thrown at platform/weixin/weixin.go:807

		slog.Error("weixin: push_path_budget_exceeded",
			"path", string(path),
			"used", len(p.sendQuotaTimes),
			"limit", p.sendQuotaLimit,
			"window", p.sendQuotaWindow.String(),
			"hint", "ilink throttles the bot after roughly 5-6 pushes per window — reduce cron/timer pushes or re-login later",
		)
		return fmt.Errorf("weixin: push budget exhausted (%d push messages in the last %s); "+
			"ilink throttles the bot after roughly 5-6 pushes per window — reduce messages or re-login later", p.sendQuotaLimit, p.sendQuotaWindow)
	}
	p.sendQuotaTimes = append(p.sendQuotaTimes, now)
	p.sendQuotaMu.Unlock()
	return nil
}

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)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Only pass reply contexts obtained from the same live weixin Platform instance
  2. If sending a fresh (non-reply) message, use the correct Send path so a valid context is built
  3. Recreate the platform and its reply contexts rather than reusing serialized/old ones

Example fix

// before
err := p.Send(nil, "hello")            // nil context
// after
rc := p.NewReplyContext(peerUserID)    // obtain a valid *replyContext
err := p.Send(rc, "hello")
Defensive patterns

Strategy: type-guard

Validate before calling

rc, ok := ctxValue.(*weixin.ReplyContext)
if !ok || rc == nil {
    return errors.New("no valid weixin reply context")
}

Type guard

func validReplyContext(v any) bool {
    rc, ok := v.(*replyContext)
    return ok && rc != nil
}

Try / catch

if err := p.Send(rc, msg); err != nil && strings.Contains(err.Error(), "invalid reply context") {
    log.Error("reply context from wrong/old platform instance; recreate it")
}

Prevention

When it happens

Trigger: Reply() or Send() invoked with a reply context that is nil or was created by a different platform type; passing a string/struct instead of the *replyContext the weixin platform returned.

Common situations: Swapping platform implementations while reusing cached reply contexts; storing a reply context across a platform restart so the old concrete type no longer matches; passing nil context into Send directly.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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