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
- Check basic connectivity: curl -v https://ilinkai.weixin.qq.com from the bot host
- If the error says 'context deadline exceeded' or 'Client.Timeout', increase the timeout config or check for a slow/hanging proxy
- Verify DNS resolution of the API host and any proxy env vars (HTTP_PROXY/HTTPS_PROXY)
- 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
- Run the bot on a host with reliable internet and working DNS
- Check HTTP(S)_PROXY settings — proxies frequently break long-polling
- Raise firewall exceptions for ilinkai.weixin.qq.com:443
- Rely on getUpdates' built-in timeout normalization instead of treating timeouts as fatal
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.