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

  1. Log the wrapped url= field to identify which request failed and inspect it with curl
  2. Check for 403/429 — back off, rotate user-agent/IP, or ensure the cookie jar carries prior session cookies
  3. Handle 404 by refreshing the source URL list; the page is gone
  4. 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

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)