{"record":{"id":"ed483689c80f9895","repo":"chenhg5/cc-connect","slug":"weixin-s-w","errorCode":null,"errorMessage":"weixin: %s: %w","messagePattern":"weixin: (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/weixin/client.go","lineNumber":104,"sourceCode":"\treq.Header.Set(\"AuthorizationType\", \"ilink_bot_token\")\n\treq.Header.Set(\"Content-Length\", fmt.Sprintf(\"%d\", len(body)))\n\treq.Header.Set(\"X-WECHAT-UIN\", randomWechatUIN())\n\tif c.token != \"\" {\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+c.token)\n\t}\n\tif c.routeTag != \"\" {\n\t\treq.Header.Set(\"SKRouteTag\", c.routeTag)\n\t}\n\n\tclient := c.httpClient\n\tif timeout > 0 {\n\t\t// Dedicated client so long-poll does not inherit short Timeout from default client.\n\t\tclient = c.longPollClient(timeout)\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"weixin: %s: %w\", label, err)\n\t}\n\tdefer resp.Body.Close()\n\traw, err := io.ReadAll(io.LimitReader(resp.Body, maxIlinkHTTPResponseBody+1))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"weixin: %s: read body: %w\", label, err)\n\t}\n\tif len(raw) > maxIlinkHTTPResponseBody {\n\t\treturn nil, fmt.Errorf(\"weixin: %s: response body exceeds %d bytes\", label, maxIlinkHTTPResponseBody)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"weixin: %s: http %d: %s\", label, resp.StatusCode, truncateForLog(raw, 512))\n\t}\n\treturn raw, nil\n}\n\nfunc truncateForLog(b []byte, max int) string {\n\ts := string(b)\n\tif len(s) <= max {","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/weixin/client.go#L86-L122","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: failing hard on transient transport errors\nif err := client.getUpdates(ctx, buf, 35000); err != nil { return err }\n// after: retry transport failures with backoff\nif err := client.getUpdates(ctx, buf, 35000); err != nil {\n    if !errors.Is(err, context.Canceled) { time.Sleep(time.Second); return client.getUpdates(ctx, buf, 35000) }\n    return err\n}","handlingStrategy":"retry","validationCode":"// pre-flight connectivity check at startup\nconn, err := net.DialTimeout(\"tcp\", \"ilinkai.weixin.qq.com:443\", 5*time.Second)\nif err != nil { slog.Warn(\"weixin API unreachable\", \"error\", err) } else { conn.Close() }","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() {\n        // long-poll timeout is normal for getUpdates; retry\n        return c.getUpdates(ctx, buf, timeoutMs)\n    }\n    if !errors.Is(err, context.Canceled) { time.Sleep(time.Second); /* retry */ }\n}","preventionTips":["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"],"tags":["network","http","timeout","weixin","dns"],"backgroundTag":"network-request-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}