{"record":{"id":"5bf45286cc629c06","repo":"chenhg5/cc-connect","slug":"weixin-s-http-d-s","errorCode":null,"errorMessage":"weixin: %s: http %d: %s","messagePattern":"weixin: (.+?): http (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/weixin/client.go","lineNumber":115,"sourceCode":"\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\nfunc (c *apiClient) getUpdates(ctx context.Context, buf string, timeoutMs int) (*getUpdatesResp, error) {\n\ttimeout := defaultLongPollTimeout\n\tif timeoutMs > 0 {\n\t\ttimeout = time.Duration(timeoutMs) * time.Millisecond\n\t}\n\treq := getUpdatesReq{","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/weixin/client.go#L97-L133","documentation":"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).","triggerScenarios":"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).","commonSituations":"Expired or revoked ilink_bot_token in config.toml; malformed send message payload; WeChat API rate limiting under burst; temporary iLink service outage (5xx).","solutions":["Read the status code and the truncated body in the error — it usually contains errcode/errmsg explaining the rejection","For 401/403, regenerate/refresh the ilink bot token in config.toml and restart","For 429, add backoff/rate limiting to outbound messages","For 5xx, retry later — transient WeChat-side failure; check service status"],"exampleFix":"// before: treating every non-200 as fatal\nif err := client.sendMessage(ctx, msg); err != nil { return err }\n// after: branch on the embedded status\nif err := client.sendMessage(ctx, msg); err != nil {\n    var se struct{ code int }\n    if _, scan := fmt.Sscanf(err.Error(), \"weixin: sendMessage: http %d\", &se.code); scan == nil && se.code == 429 {\n        time.Sleep(time.Minute); return client.sendMessage(ctx, msg)\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// verify the token before starting the engine\nif strings.TrimSpace(token) == \"\" { return fmt.Errorf(\"weixin: missing bot token\") }\n// optionally: dry-run an authenticated call at startup","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var httpErr struct{ status int }\n    if n, _ := fmt.Sscanf(err.Error(), \"weixin: %*s: http %d\", &httpErr.status); n == 1 {\n        switch {\n        case httpErr.status == 401 || httpErr.status == 403: // refresh token\n        case httpErr.status == 429: // backoff\n        default: // log and retry later\n        }\n    }\n}","preventionTips":["Keep the ilink bot token valid and rotate before expiry","Implement backoff for 429s and circuit-breaking for sustained 5xx","Validate outgoing message payloads (non-empty item_list, valid to_user_id) to avoid 400s","Alert on any spike of non-200 responses from the iLink API"],"tags":["http","api","weixin","status-code","auth"],"backgroundTag":"http-non-200-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}