fish2018/pansou · error
[ ] HTTP状态码异常: url=
Error message
[%s] HTTP状态码异常: %d url=%s
What it means
fetchBody requires HTTP 200 for every request. Any other status (403 from the anti-bot, 404 for a moved detail page, 5xx, redirects that end non-200) aborts with this error, which embeds the status code and the requested URL.
Solutions
- Log the wrapped url= field to identify which request failed and inspect it with curl
- Check for 403/429 — back off, rotate user-agent/IP, or ensure the cookie jar carries prior session cookies
- Handle 404 by refreshing the source URL list; the page is gone
- Retry later on 5xx; getDetailInfo already tries alternate candidate URLs
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check availability of a detail URL before full processing
resp, err := client.Head(detailURL)
if err != nil || resp.StatusCode != 200 {
log.Printf("url %s unavailable (status=%v)", detailURL, statusOrErr(resp, err))
} Try / catch
info, err := plugin.GetDetailInfo(ctx, url)
if err != nil {
var statusErr interface{ Error() string }
if strings.Contains(err.Error(), "HTTP状态码异常") {
if strings.Contains(err.Error(), " 403 ") || strings.Contains(err.Error(), " 429 ") {
time.Sleep(rateLimitBackoff) // blocked/rate-limited
} else if strings.Contains(err.Error(), " 404 ") {
return ErrPageGone // do not retry
}
}
} Prevention
- Branch on the status code embedded in the message: 403/429 => back off, 404 => drop the URL, 5xx => retry later
- Use a realistic User-Agent and keep the cookie jar so anti-bot gates pass
- Respect the site's rate limits; getDetailInfo already alternates candidate URLs on failure
When it happens
Trigger: resp.StatusCode != http.StatusOK after doRequestWithRetry in fetchBody, invoked by searchSuggest, getDetailInfo, or solveVerification — e.g. the site returns 403/429 when anti-bot protection triggers, or 404 for a dead detail URL.
Common situations: Site blocks the client's IP or user-agent with 403/429; detail page removed (404); origin 5xx during deploys; missing cookies so the anti-bot gate returns non-200.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/24a25f01201e7d84.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/qiwei/qiwei.go:628
func (p *QiweiPlugin) fetchBody(client *http.Client, requestURL, referer string, timeout time.Duration) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return "", fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
p.setHeaders(req, referer)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("[%s] HTTP状态码异常: %d url=%s", p.Name(), resp.StatusCode, requestURL)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
return normalizeResponseBody(string(body)), nil
}
func (p *QiweiPlugin) doRequestWithRetry(req *http.Request, client *http.Client) (*http.Response, error) {
const maxRetries = 3
var lastErr error
for i := 0; i < maxRetries; i++ {
if i > 0 {
time.Sleep(time.Duration(1<<uint(i-1)) * 200 * time.Millisecond)
}View on GitHub (pinned to beaa561337)