chenhg5/cc-connect · error

weixin: %s: %w

Error message

weixin: %s: %w

What it means

post() fails when the HTTP client cannot complete the request: DNS failure, TCP connect error, TLS handshake failure, or a client-side timeout (long-poll uses a dedicated client with timeout+5s). The underlying *url.Error is wrapped so its text names the transport-level cause. Callers like getUpdates translate context deadline/net timeouts into an empty poll result instead of propagating this.

Source

Thrown at platform/weixin/client.go:104

	req.Header.Set("AuthorizationType", "ilink_bot_token")
	req.Header.Set("Content-Length", fmt.Sprintf("%d", len(body)))
	req.Header.Set("X-WECHAT-UIN", randomWechatUIN())
	if c.token != "" {
		req.Header.Set("Authorization", "Bearer "+c.token)
	}
	if c.routeTag != "" {
		req.Header.Set("SKRouteTag", c.routeTag)
	}

	client := c.httpClient
	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 {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check basic connectivity: curl -v https://ilinkai.weixin.qq.com from the bot host
  2. If the error says 'context deadline exceeded' or 'Client.Timeout', increase the timeout config or check for a slow/hanging proxy
  3. Verify DNS resolution of the API host and any proxy env vars (HTTP_PROXY/HTTPS_PROXY)
  4. For getUpdates specifically, note timeouts are normalized to an empty poll — only non-timeout transport errors surface here

Example fix

// before: failing hard on transient transport errors
if err := client.getUpdates(ctx, buf, 35000); err != nil { return err }
// after: retry transport failures with backoff
if err := client.getUpdates(ctx, buf, 35000); err != nil {
    if !errors.Is(err, context.Canceled) { time.Sleep(time.Second); return client.getUpdates(ctx, buf, 35000) }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight connectivity check at startup
conn, err := net.DialTimeout("tcp", "ilinkai.weixin.qq.com:443", 5*time.Second)
if err != nil { slog.Warn("weixin API unreachable", "error", err) } else { conn.Close() }

Try / catch

if err != nil {
    var ne net.Error
    if errors.As(err, &ne) && ne.Timeout() {
        // long-poll timeout is normal for getUpdates; retry
        return c.getUpdates(ctx, buf, timeoutMs)
    }
    if !errors.Is(err, context.Canceled) { time.Sleep(time.Second); /* retry */ }
}

Prevention

When it happens

Trigger: client.Do returns a non-nil error — server unreachable, DNS failure, TLS error, or Timeout exceeded on either the default 15s client or the long-poll client.

Common situations: No internet/DNS on the host; firewall blocking ilinkai.weixin.qq.com; long-poll timing out behind a proxy that cuts connections early; corporate TLS interception breaking the handshake.

Related errors


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