{"record":{"id":"2e1fd03edcd38c85","repo":"fish2018/pansou","slug":"200-d","errorCode":null,"errorMessage":"服务器返回非200状态码: %d","messagePattern":"服务器返回非200状态码: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/pansearch/pansearch.go","lineNumber":615,"sourceCode":"\treq.Header.Set(\"Accept-Language\", \"zh-CN,zh;q=0.9,en;q=0.8\")\n\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Cache-Control\", \"no-cache\")\n\treq.Header.Set(\"Pragma\", \"no-cache\")\n\n\t// 发送请求\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 检查状态码\n\tif resp.StatusCode == 404 {\n\t\treturn nil, 0, fmt.Errorf(\"404 Not Found，buildId可能已过期\")\n\t}\n\n\tif resp.StatusCode != 200 {\n\t\treturn nil, 0, fmt.Errorf(\"服务器返回非200状态码: %d\", resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\t// 解析响应\n\tvar apiResp PanSearchResponse\n\tif err := json.Unmarshal(respBody, &apiResp); err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\t// 获取total和结果\n\ttotal := apiResp.PageProps.Data.Total\n\titems := apiResp.PageProps.Data.Data\n","sourceCodeStart":597,"sourceCodeEnd":633,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/pansearch/pansearch.go#L597-L633","documentation":"fetchFirstPage rejects any pansearch.me response whose status code is not 200 (after handling 404 separately). This wraps any unexpected HTTP status from the upstream Next.js data API (3xx redirect terminal responses, 403 bot-blocking, 429 rate limiting, 5xx outages) into a descriptive error carrying the numeric code.","triggerScenarios":"fetchFirstPage (called by doSearch) receives resp.StatusCode not in {200, 404} from the pansearch.me /_next/data request, e.g. 403 from Cloudflare, 429 throttling, or 500/502/503 from the upstream server.","commonSituations":"Pansearch.me blocks non-browser traffic with 403; scraping too frequently triggers 429; the upstream site is down or deploying (5xx); a CDN edge returns an unusual status.","solutions":["Log resp.StatusCode and a body snippet to identify the exact upstream cause (403 vs 429 vs 5xx)","Add exponential backoff retry for 429/5xx statuses","Send full browser-like headers (User-Agent, Referer, Accept, Cookie) as fetchPage does, to avoid bot-blocking 403","Throttle request rate to the upstream site; if persistent, treat the site as temporarily unavailable and degrade gracefully"],"exampleFix":"// before\nif resp.StatusCode != 200 {\n    return nil, 0, fmt.Errorf(\"服务器返回非200状态码: %d\", resp.StatusCode)\n}\n// after\nif resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {\n    return nil, 0, retryableStatusError{code: resp.StatusCode}\n}\nif resp.StatusCode != 200 {\n    return nil, 0, fmt.Errorf(\"服务器返回非200状态码: %d, body: %.200s\", resp.StatusCode, safeBodySnippet(resp))\n}","handlingStrategy":"retry","validationCode":"// pre-flight reachability probe before batch searching:\n// resp, err := client.Get(\"https://www.pansearch.me/\")\n// if err == nil && (resp.StatusCode == 403 || resp.StatusCode == 429) { backoff before proceeding }","typeGuard":"func isRetryableStatus(code int) bool {\n    return code == http.StatusTooManyRequests || code >= 500\n}","tryCatchPattern":"items, total, err := p.fetchFirstPage(ctx, query)\nif err != nil {\n    var statusErr *statusError\n    if errors.As(err, &statusErr) && isRetryableStatus(statusErr.Code) {\n        time.Sleep(backoff(attempt))\n        items, total, err = p.fetchFirstPage(ctx, query)\n    }\n}\nif err != nil { return err }","preventionTips":["Throttle request rate to the upstream site","Send complete browser-like headers (User-Agent, Referer, Accept)","Log the status code and body snippet for every non-200","Back off exponentially on 429/5xx instead of hammering"],"tags":["http","scraping","upstream","status-code"],"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"}