chenhg5/cc-connect · error

weixin: push budget exhausted (%d push messages in the last

Error message

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

What it means

checkSendQuota enforces a sliding-window push budget because the ilink backend throttles the bot after roughly 5-6 pushed (non-reply) messages per window. When the number of recorded pushes within sendQuotaWindow reaches the limit, further pushes are rejected with this explanatory error.

Source

Thrown at platform/weixin/weixin.go:796

	cutoff := now.Add(-p.sendQuotaWindow)
	kept := p.sendQuotaTimes[:0]
	for _, t := range p.sendQuotaTimes {
		if t.After(cutoff) {
			kept = append(kept, t)
		}
	}
	p.sendQuotaTimes = kept
	if len(p.sendQuotaTimes) >= p.sendQuotaLimit {
		p.sendQuotaMu.Unlock()
		pushBudgetExceededCounter.Add(1)
		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)
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Reduce the number of pushed messages per window or batch content into one message
  2. Increase sendQuotaLimit via configuration if acceptable for the account
  3. Have the user message the bot and use Reply (replies bypass the push quota)
  4. Wait until the sliding window expires and re-send
Defensive patterns

Strategy: retry

Validate before calling

// check quota pressure before pushing
count, window := p.PushQuotaUsage() // if exposed
if count >= limit {
    return fmt.Errorf("push budget %d/%d used in %s; defer send", count, limit, window)
}

Try / catch

err := p.Send(ctx, rc, content)
if err != nil && strings.Contains(err.Error(), "push budget exhausted") {
    time.Sleep(window)
    return p.Send(ctx, rc, content)
}

Prevention

When it happens

Trigger: Send() (push path) is called more than sendQuotaLimit times within sendQuotaWindow, e.g. cron/timer jobs firing several messages back-to-back. Reply() bypasses this quota via sendPath.

Common situations: Multiple cron jobs or timers dispatching results simultaneously; a long agent output chunked into several pushes; burst of automated notifications after re-login; quota limits left at defaults while message volume grew.

Related errors


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