fish2018/pansou · error
[ ] 第 页请求返回状态码
Error message
[%s] 第%d页请求返回状态码: %d
What it means
searchPage requires HTTP 200 from the search endpoint. Any other status (403 bot-block, 404 gone mirror, 429 rate limit, 5xx server error) produces this error carrying the page number and status code. Unlike 723, the request succeeded at transport level — the server answered with a non-200 status.
Solutions
- Log the response body snippet for the failing status to identify bot challenges vs dead pages.
- Switch to a working mirror domain in the plugin configuration/constants.
- Slow down request rate or add jitter between page fetches to avoid 429s.
- Ensure realistic browser headers (User-Agent, Accept, Referer) are present — they already are; consider cookie handling for challenge pages.
- Handle 5xx with the existing retry mechanism by treating retryable statuses in doRequestWithRetry.
Example fix
// before
if resp.StatusCode != 200 {
return nil, 0, fmt.Errorf("[%s] 第%d页请求返回状态码: %d", p.Name(), page, resp.StatusCode)
}
// after: retry transient statuses
code := resp.StatusCode
if code == 429 || code >= 500 {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
time.Sleep(2 * time.Second)
return p.searchPage(ctx, keyword, page) // bounded by caller
}
if code != 200 {
return nil, 0, fmt.Errorf("[%s] 第%d页请求返回状态码: %d", p.Name(), page, code)
} Defensive patterns
Strategy: fallback
Validate before calling
resp, err := http.Head(searchURL)
if err == nil && resp.StatusCode != 200 {
return fmt.Errorf("mirror returned %d before searching, switch mirror", resp.StatusCode)
} Type guard
func isRetryableStatus(code int) bool {
return code == 429 || code >= 500
}
func isBlockStatus(code int) bool {
return code == 403 || code == 503
} Try / catch
results, total, err := plugin.Search(keyword, page)
var statusErr error
if err != nil && strings.Contains(err.Error(), "请求返回状态码") {
if strings.Contains(err.Error(), ": 403") || strings.Contains(err.Error(), ": 503") {
results, total, err = searchViaMirror(keyword, page) // bot-blocked: try alternate mirror/resolver
} else {
results, total, err = retryWithBackoff(2, func() ([]model.Item, int, error) {
return plugin.Search(keyword, page)
})
}
} Prevention
- Keep a curated mirror list and probe it periodically.
- Rate-limit requests with jitter so anti-bot/429 protections are not triggered.
- Send full browser-like headers and reuse cookies across requests.
- Log status + a small body snippet to distinguish blocking from outage.
When it happens
Trigger: doRequestWithRetry returns a response whose StatusCode != 200 during a searchPage call: Cloudflare/anti-bot challenge page, mirror moved (404), too many rapid requests (429), or upstream outage (500/502/503).
Common situations: Mirror domain blocked/changed so requests hit a parking page; scraping too fast triggers rate limiting; missing/rotated User-Agent gets flagged by anti-bot; the .xyz mirror is dead.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/d20fed98eeca5513.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/thepiratebay/thepiratebay.go:272
// 5. 设置完整的请求头 - 参考插件开发指南的最佳实践
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Upgrade-Insecure-Requests", "1")
req.Header.Set("Cache-Control", "max-age=0")
req.Header.Set("Referer", "https://thpibay.xyz/")
// 6. 发送HTTP请求(带重试机制)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, 0, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
}
defer resp.Body.Close()
// 7. 检查状态码
if resp.StatusCode != 200 {
return nil, 0, fmt.Errorf("[%s] 第%d页请求返回状态码: %d", p.Name(), page, resp.StatusCode)
}
// 8. 解析HTML响应
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, 0, fmt.Errorf("[%s] 第%d页HTML解析失败: %w", p.Name(), page, err)
}
// 9. 解析分页信息(只在第一页解析)
totalPages := 1
if page == 1 {
totalPages = p.parseTotalPages(doc)
}
// 10. 提取搜索结果
results := make([]model.SearchResult, 0)
doc.Find("table#searchResult tr").Each(func(i int, s *goquery.Selection) {
// 跳过表头View on GitHub (pinned to beaa561337)