{"record":{"id":"b5e69a87cc1a8efa","repo":"fish2018/pansou","slug":"d-b5e69a","errorCode":null,"errorMessage":"状态码 %d","messagePattern":"状态码 %d","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xiaoyu/xiaoyu.go","lineNumber":194,"sourceCode":"}\n\nfunc doRequestWithRetry(client *http.Client, req *http.Request) (*http.Response, error) {\n\tvar lastErr error\n\tfor attempt := 0; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 0 {\n\t\t\ttime.Sleep(time.Duration(attempt) * 200 * time.Millisecond)\n\t\t}\n\n\t\tresp, err := client.Do(req.Clone(req.Context()))\n\t\tif err != nil {\n\t\t\tlastErr = err\n\t\t\tcontinue\n\t\t}\n\t\tif resp.StatusCode == http.StatusOK {\n\t\t\treturn resp, nil\n\t\t}\n\n\t\tlastErr = fmt.Errorf(\"状态码 %d\", resp.StatusCode)\n\t\tresp.Body.Close()\n\t}\n\treturn nil, lastErr\n}\n\nfunc parsePage(doc *goquery.Document) pageResult {\n\tresult := pageResult{TotalPages: parseTotalPages(doc)}\n\tdoc.Find(\".search-list .item\").Each(func(_ int, item *goquery.Selection) {\n\t\tparsed, ok := parseItem(item)\n\t\tif ok {\n\t\t\tresult.Results = append(result.Results, parsed)\n\t\t}\n\t})\n\treturn result\n}\n\nfunc parseTotalPages(doc *goquery.Document) int {\n\tpageText := cleanText(doc.Find(\".count strong\").Eq(1).Text())","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xiaoyu/xiaoyu.go#L176-L212","documentation":"doRequestWithRetry treats any non-200 status as a retriable failure. When a response arrives with a status other than 200, it constructs this plain error (no wrapped cause) as lastErr, closes the body, and retries; if all attempts fail, this error is returned to fetchPage and surfaces as error 817.","triggerScenarios":"The Xiaoyu search endpoint responded with a status other than 200 (403 anti-bot block, 429 rate limit, 404 for unsupported page numbers, 5xx server errors) on every retry attempt.","commonSituations":"IP blocked by the site's WAF after aggressive scraping; request rate exceeded limits; missing/stale cookies or headers causing 403; requesting a page index the site does not serve; transient 5xx during site maintenance.","solutions":["Log the status code and response headers/body snippet to identify 403 vs 429 vs 5xx and act accordingly.","For 429, add exponential backoff and respect any Retry-After header before retrying.","For 403, refresh cookies/User-Agent/headers in setRequestHeaders to look like a real browser.","Reduce request concurrency and add pacing to avoid triggering blocks.","Retry later for 5xx — the failure is server-side."],"exampleFix":"// before\nlastErr = fmt.Errorf(\"状态码 %d\", resp.StatusCode)\nresp.Body.Close()\n// after\nlastErr = fmt.Errorf(\"状态码 %d (%s)\", resp.StatusCode, http.StatusText(resp.StatusCode))\nif resp.StatusCode == http.StatusTooManyRequests {\n\tselect {\n\tcase <-time.After(backoff):\n\tcase <-req.Context().Done():\n\t\treturn nil, req.Context().Err()\n\t}\n}\nresp.Body.Close()","handlingStrategy":"retry","validationCode":"req.Header.Set(\"User-Agent\", realisticUA)\nreq.Header.Set(\"Accept\", \"text/html,application/xhtml+xml\")\n// send a HEAD/probe first and bail out early on 403/429\nprobe, _ := client.Head(siteURL)\nif probe != nil && (probe.StatusCode == 403 || probe.StatusCode == 429) {\n\treturn fmt.Errorf(\"blocked upstream, status %d\", probe.StatusCode)\n}","typeGuard":"func isTransientStatus(code int) bool { return code == 429 || code >= 500 }","tryCatchPattern":"resp, err := doRequestWithRetry(client, req)\nif err != nil {\n\tif strings.Contains(err.Error(), \"429\") || strings.Contains(err.Error(), \"403\") {\n\t\ttime.Sleep(respectBackoff)\n\t}\n\treturn pageResult{}, err\n}","preventionTips":["Back off exponentially on 429 and honor Retry-After headers.","Refresh cookies/User-Agent when 403s start appearing.","Throttle concurrency and add per-request pacing to avoid WAF blocks.","Retry only transient statuses (429/5xx); fail fast on 403/404."],"tags":["http","status-code","rate-limit","retry"],"backgroundTag":"http-error-status","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}