{"record":{"id":"7aad262746fb61a0","repo":"fish2018/pansou","slug":"d-7aad26","errorCode":null,"errorMessage":"请求返回状态码: %d","messagePattern":"请求返回状态码: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xdpan/xdpan.go","lineNumber":119,"sourceCode":"\treq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"创建GET请求失败: %w\", err)\n\t}\n\n\tp.setRequestHeaders(req)\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[xdpan] 搜索URL: %s\\n\", searchURL)\n\t}\n\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"GET请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"请求返回状态码: %d\", resp.StatusCode)\n\t}\n\n\t// 解析HTML\n\tdoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxPageSize))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"解析HTML失败: %w\", err)\n\t}\n\n\tresults := p.extractSearchResults(doc)\n\tif len(results) == 0 && doc.Find(\"title\").First().Text() == \"Just a moment...\" {\n\t\treturn nil, fmt.Errorf(\"站点触发 Cloudflare 浏览器验证\")\n\t}\n\treturn results, nil\n}\n\n// extractSearchResults 从搜索页面提取结果\nfunc (p *XdpanPlugin) extractSearchResults(doc *goquery.Document) []model.SearchResult {\n\tvar results []model.SearchResult","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xdpan/xdpan.go#L101-L137","documentation":"fetchSearchResults in the xdpan plugin performs an HTTP GET to the search site and requires HTTP 200. Any other status code aborts parsing and is returned as this error, so the developer sees only the numeric status instead of the response body.","triggerScenarios":"Any non-200 response from the search endpoint during searchImpl: 403/503 from Cloudflare or WAF, 404 after a site URL/path change, 429 rate limiting, 5xx upstream outage.","commonSituations":"Site migrated behind Cloudflare bot protection; too-frequent scraping triggering 429; stale baseURL pointing to a dead page; server temporarily down.","solutions":["Log resp.StatusCode and dump a snippet of resp.Body before returning to identify the blocker","Check the cf-mitigated response header to detect Cloudflare challenges and switch to a browser-like flow","Slow down request rate and honor Retry-After for 429","Update/verify baseURL and search path if the site changed","Use doRequestWithRetry which already retries transient non-200s"],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    return nil, fmt.Errorf(\"请求返回状态码: %d\", resp.StatusCode)\n}\n// after\nif resp.StatusCode != http.StatusOK {\n    body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\n    return nil, fmt.Errorf(\"请求返回状态码: %d, 响应: %s\", resp.StatusCode, body)\n}","handlingStrategy":"try-catch","validationCode":"// Go has no pre-call validation for remote status; probe first:\nresp, err := client.Head(searchURL)\nif err == nil && resp.StatusCode != http.StatusOK {\n    return fmt.Errorf(\"站点当前不可用: %d\", resp.StatusCode)\n}","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(ctx, kw)\nif err != nil {\n    var statusErr *fmt.StatusError // or match on the message\n    if strings.Contains(err.Error(), \"请求返回状态码\") {\n        // degrade gracefully: return empty results and notify\n        return fallbackResults, nil\n    }\n    return err\n}","preventionTips":["Monitor target site status and Cloudflare changes","Throttle request rate to avoid 429","Keep baseURL and paths up to date","Log response body snippets on non-200"],"tags":["network","http","scraping"],"backgroundTag":"http-error-response","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"}