chenhg5/cc-connect · error

weixin: %s: http %d: %s

Error message

weixin: %s: http %d: %s

What it means

post() fails when the iLink API returns any status other than 200. The first 512 bytes of the response body (via truncateForLog) are appended because WeChat usually embeds ret/errcode/errmsg diagnostics in the body. This is the generic HTTP-level failure for all API calls (getUpdates, sendMessage, getUploadUrl, getConfig, sendTyping).

Source

Thrown at platform/weixin/client.go:115

	if timeout > 0 {
		// Dedicated client so long-poll does not inherit short Timeout from default client.
		client = c.longPollClient(timeout)
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("weixin: %s: %w", label, err)
	}
	defer resp.Body.Close()
	raw, err := io.ReadAll(io.LimitReader(resp.Body, maxIlinkHTTPResponseBody+1))
	if err != nil {
		return nil, fmt.Errorf("weixin: %s: read body: %w", label, err)
	}
	if len(raw) > maxIlinkHTTPResponseBody {
		return nil, fmt.Errorf("weixin: %s: response body exceeds %d bytes", label, maxIlinkHTTPResponseBody)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("weixin: %s: http %d: %s", label, resp.StatusCode, truncateForLog(raw, 512))
	}
	return raw, nil
}

func truncateForLog(b []byte, max int) string {
	s := string(b)
	if len(s) <= max {
		return s
	}
	return s[:max] + "…"
}

func (c *apiClient) getUpdates(ctx context.Context, buf string, timeoutMs int) (*getUpdatesResp, error) {
	timeout := defaultLongPollTimeout
	if timeoutMs > 0 {
		timeout = time.Duration(timeoutMs) * time.Millisecond
	}
	req := getUpdatesReq{

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the status code and the truncated body in the error — it usually contains errcode/errmsg explaining the rejection
  2. For 401/403, regenerate/refresh the ilink bot token in config.toml and restart
  3. For 429, add backoff/rate limiting to outbound messages
  4. For 5xx, retry later — transient WeChat-side failure; check service status

Example fix

// before: treating every non-200 as fatal
if err := client.sendMessage(ctx, msg); err != nil { return err }
// after: branch on the embedded status
if err := client.sendMessage(ctx, msg); err != nil {
    var se struct{ code int }
    if _, scan := fmt.Sscanf(err.Error(), "weixin: sendMessage: http %d", &se.code); scan == nil && se.code == 429 {
        time.Sleep(time.Minute); return client.sendMessage(ctx, msg)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the token before starting the engine
if strings.TrimSpace(token) == "" { return fmt.Errorf("weixin: missing bot token") }
// optionally: dry-run an authenticated call at startup

Try / catch

if err != nil {
    var httpErr struct{ status int }
    if n, _ := fmt.Sscanf(err.Error(), "weixin: %*s: http %d", &httpErr.status); n == 1 {
        switch {
        case httpErr.status == 401 || httpErr.status == 403: // refresh token
        case httpErr.status == 429: // backoff
        default: // log and retry later
        }
    }
}

Prevention

When it happens

Trigger: Any ilink/bot/* POST returning 4xx/5xx — invalid or expired bot token (401/403), bad request payload (400), rate limiting (429), or server errors (5xx).

Common situations: Expired or revoked ilink_bot_token in config.toml; malformed send message payload; WeChat API rate limiting under burst; temporary iLink service outage (5xx).

Related errors


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