{"record":{"id":"f0e37a08d70d64a8","repo":"fish2018/pansou","slug":"s-d-f0e37a","errorCode":null,"errorMessage":"[%s] 请求返回状态码: %d","messagePattern":"\\[(.+?)\\] 请求返回状态码: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/kkv/kkv.go","lineNumber":137,"sourceCode":"\tdefer cancel()\n\t\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\t\n\tp.setHeaders(req, baseURL)\n\t\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\t\n\tdebugPrintf(\"📡 HTTP状态码: %d\\n\", resp.StatusCode)\n\t\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\t\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] HTML解析失败: %w\", p.Name(), err)\n\t}\n\t\n\tvar items []searchItem\n\tdoc.Find(\"article.post\").Each(func(i int, s *goquery.Selection) {\n\t\tlink := s.Find(\".entry-header h2.entry-title a\")\n\t\thref, exists := link.Attr(\"href\")\n\t\tif !exists {\n\t\t\tdebugPrintf(\"⚠️ 第%d个结果没有href属性\\n\", i+1)\n\t\t\treturn\n\t\t}\n\t\t\n\t\ttitle := strings.TrimSpace(link.Text())\n\t\tif title == \"\" {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/kkv/kkv.go#L119-L155","documentation":"The kkv plugin rejects any search response whose HTTP status is not 200. After retries succeeded at the transport level, the server returned a non-200 status (e.g. 403 anti-bot, 404 wrong path, 5xx). The plugin treats this as a search failure rather than trying to parse the body.","triggerScenarios":"doRequestWithRetry returns a response whose resp.StatusCode != 200 for the kkv search URL — most commonly 403 from anti-scraping (Cloudflare), 404 after the site changed its URL structure, or 5xx while the site is degraded.","commonSituations":"Cloudflare/WAF blocking datacenter IPs (403/429), the source site changed its search path so the old URL 404s, rate limiting after heavy scraping, site maintenance returning 503.","solutions":["Log resp.StatusCode (the message already contains it) and check which status it is before changing anything","For 403/429: add realistic headers/cookies via setHeaders, slow down request rate, or route through a residential proxy","For 404: inspect the site and update the search URL pattern in the plugin","For 5xx: retry later; the upstream site is degraded"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(ctx, kw)\nif err != nil {\n    var statusErr interface{ HTTPStatus() int } // or match the '[%s] 请求返回状态码: %d' text\n    if strings.Contains(err.Error(), \"请求返回状态码\") {\n        // non-200 from upstream: fall back to another source instead of failing the request\n        return fallbackSource.Search(ctx, kw)\n    }\n    return err\n}","preventionTips":["Throttle request rate per source to avoid 429 bans","Route through residential proxies when datacenter IPs get 403s","Monitor status codes per source and alert on rising 403/5xx rates","Keep source URL patterns updated when sites redesign"],"tags":["http","status-code","scraping","anti-bot"],"backgroundTag":"http-non-200-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"}