{"record":{"id":"e53ebf558f4d51bd","repo":"chenhg5/cc-connect","slug":"weixin-s-read-body-w","errorCode":null,"errorMessage":"weixin: %s: read body: %w","messagePattern":"weixin: (.+?): read body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/weixin/client.go","lineNumber":109,"sourceCode":"\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 {\n\t\treturn s\n\t}\n\treturn s[:max] + \"…\"\n}\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/weixin/client.go#L91-L127","documentation":"post() fails while reading the response body with io.ReadAll (bounded by maxIlinkHTTPResponseBody = 64 MiB via io.LimitReader). This wraps I/O errors from the connection while draining the body, e.g. the server or an intermediary closed the connection mid-response.","triggerScenarios":"io.ReadAll returns an error during body read — abrupt TCP reset, connection closed early by the server/proxy, or read deadline expiry mid-body.","commonSituations":"Server crashes or load balancer resets mid-response; proxy killing long responses; flaky mobile/hotspot networking on the host running cc-connect.","solutions":["Retry the request — the error is usually transient connection breakage","Check for intermediary proxies/LBs with aggressive idle timeouts and raise them","Inspect the wrapped %w error (e.g. 'unexpected EOF', 'connection reset') to identify the transport cause","Ensure stable network for the host running cc-connect; log the label to know which API call failed"],"exampleFix":"// before: one-shot post\nraw, err := c.post(ctx, \"ilink/bot/getupdates\", payload, timeout, \"getUpdates\")\n// after: single retry on body-read failure\nraw, err := c.post(ctx, \"ilink/bot/getupdates\", payload, timeout, \"getUpdates\")\nif err != nil && strings.Contains(err.Error(), \"read body\") {\n    raw, err = c.post(ctx, \"ilink/bot/getupdates\", payload, timeout, \"getUpdates\")\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"raw, err := c.post(ctx, ep, payload, timeout, label)\nif err != nil && strings.Contains(err.Error(), \"read body\") {\n    time.Sleep(500 * time.Millisecond)\n    raw, err = c.post(ctx, ep, payload, timeout, label) // one retry\n}","preventionTips":["Keep connections on stable networks; avoid flaky uplinks for the bot host","Raise idle/read timeouts on any intermediary proxy or load balancer","Keep getUpdates batches small by polling regularly","Check the wrapped cause ('unexpected EOF', 'reset by peer') to target the right network fix"],"tags":["network","http","io","weixin","connection-reset"],"backgroundTag":"file-read-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"}