fish2018/pansou · error
搜索响应状态码异常
Error message
搜索响应状态码异常: %d
What it means
The xiaozhang plugin's searchImpl got a valid HTTP response from the search endpoint, but the status code was not 200. The plugin only treats HTTP 200 as a successful search result page; anything else (3xx followed into an unexpected page, 4xx anti-bot block, 5xx server error) triggers this error with the numeric status embedded. It indicates the upstream site responded but refused or failed the search request.
Solutions
- Log the full status code and response body snippet to identify which non-200 class is returned.
- For 403/429, update request headers (User-Agent, cookies) in setRequestHeaders to look less like a bot, or add rate limiting/backoff.
- For 404, verify SearchPath against the current site layout and update the constant.
- For 5xx, retry with exponential backoff — the upstream may be temporarily down.
- Check whether the site now requires cookies/JS challenge (Cloudflare etc.) and handle accordingly.
Example fix
// before
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("搜索响应状态码异常: %d", resp.StatusCode)
}
// after
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
return nil, fmt.Errorf("搜索响应状态码异常: %d, body: %s", resp.StatusCode, string(body))
} Defensive patterns
Strategy: retry
Validate before calling
// Go: probe the endpoint and inspect status before trusting search results
resp, err := http.Head(searchURL)
if err != nil {
return fmt.Errorf("probe failed: %w", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("upstream unhealthy, status %d", resp.StatusCode)
} Type guard
// Go: distinguish rate-limit/blocks from server errors
func classifyStatus(code int) string {
switch {
case code == 200:
return "ok"
case code == 429 || code == 403:
return "blocked"
case code >= 500:
return "server-error"
default:
return "other"
}
} Try / catch
results, err := plugin.Search(keyword, ext)
if err != nil {
var statusErr *statusCodeError // if plugin exposes a typed error; otherwise parse message
if strings.Contains(err.Error(), "状态码异常") {
// non-200: back off then retry; if still blocked, skip this source
time.Sleep(backoff)
results, err = plugin.Search(keyword, ext)
}
if err != nil {
log.Printf("xinjuc/xiaozhang search skipped: %v", err)
}
} Prevention
- Send realistic browser headers (User-Agent, Accept, Referer) to reduce 403 anti-bot responses.
- Rate-limit scraping requests per host to avoid 429s.
- Monitor status codes per plugin and alert on sustained non-200 rates.
- Verify SearchPath constants still exist on the live site after site redesigns.
- Treat 5xx as retryable and 4xx as configuration/blocks — handle them differently.
When it happens
Trigger: Calling Search on the xiaozhang plugin when the search URL returns any non-200 status: 403/429 from anti-crawler WAF, 404 after a site path change, 500/502/503 server errors, or a redirect chain that lands on a non-200 page (redirects are followed since followRedirect=true).
Common situations: The site deployed anti-bot protection and starts serving 403/429 to the plugin's User-Agent; the site restructured and SearchPath now 404s; the site is rate-limiting after frequent scraping; or a CDN serves 5xx during an outage.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/cf40a2ad0cc0af5f.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/xiaozhang/xiaozhang.go:151
// searchImpl 实际的搜索实现
func (p *XiaozhangPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {
searchURL := fmt.Sprintf("%s%s?keyword=%s", BaseURL, SearchPath, url.QueryEscape(keyword))
if p.debugMode {
log.Printf("[Xiaozhang] 开始搜索: %s", keyword)
log.Printf("[Xiaozhang] 搜索URL: %s", searchURL)
}
// 发送搜索请求
resp, err := p.doRequest(client, searchURL, BaseURL, true)
if err != nil {
return nil, fmt.Errorf("发送搜索请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("搜索响应状态码异常: %d", resp.StatusCode)
}
// 处理响应体(可能是gzip压缩的)
var reader io.Reader = resp.Body
// 检查Content-Encoding
contentEncoding := resp.Header.Get("Content-Encoding")
if p.debugMode {
log.Printf("[Xiaozhang] Content-Encoding: %s", contentEncoding)
log.Printf("[Xiaozhang] Content-Type: %s", resp.Header.Get("Content-Type"))
}
// 如果是gzip压缩,手动解压
if contentEncoding == "gzip" {
gzReader, err := gzip.NewReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("创建gzip reader失败: %w", err)
}View on GitHub (pinned to beaa561337)