fish2018/pansou · error
服务器返回非200状态码
Error message
服务器返回非200状态码: %d
What it means
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.
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
Example fix
// before
if resp.StatusCode != 200 {
return nil, 0, fmt.Errorf("服务器返回非200状态码: %d", resp.StatusCode)
}
// after
if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {
return nil, 0, retryableStatusError{code: resp.StatusCode}
}
if resp.StatusCode != 200 {
return nil, 0, fmt.Errorf("服务器返回非200状态码: %d, body: %.200s", resp.StatusCode, safeBodySnippet(resp))
} Defensive patterns
Strategy: retry
Validate before calling
// pre-flight reachability probe before batch searching:
// resp, err := client.Get("https://www.pansearch.me/")
// if err == nil && (resp.StatusCode == 403 || resp.StatusCode == 429) { backoff before proceeding } Type guard
func isRetryableStatus(code int) bool {
return code == http.StatusTooManyRequests || code >= 500
} Try / catch
items, total, err := p.fetchFirstPage(ctx, query)
if err != nil {
var statusErr *statusError
if errors.As(err, &statusErr) && isRetryableStatus(statusErr.Code) {
time.Sleep(backoff(attempt))
items, total, err = p.fetchFirstPage(ctx, query)
}
}
if err != nil { return err } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/2e1fd03edcd38c85.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/pansearch/pansearch.go:615
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("Pragma", "no-cache")
// 发送请求
resp, err := client.Do(req)
if err != nil {
return nil, 0, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
// 检查状态码
if resp.StatusCode == 404 {
return nil, 0, fmt.Errorf("404 Not Found,buildId可能已过期")
}
if resp.StatusCode != 200 {
return nil, 0, fmt.Errorf("服务器返回非200状态码: %d", resp.StatusCode)
}
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, 0, fmt.Errorf("读取响应失败: %w", err)
}
// 解析响应
var apiResp PanSearchResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
return nil, 0, fmt.Errorf("解析响应失败: %w", err)
}
// 获取total和结果
total := apiResp.PageProps.Data.Total
items := apiResp.PageProps.Data.Data
View on GitHub (pinned to beaa561337)