fish2018/pansou · error

[ ] 请求返回状态码

Error message

[%s] 请求返回状态码: %d

What it means

The ASH search endpoint responded, but with a status code other than 200. searchImpl treats any non-200 as a hard failure and reports the code so the caller can react. Body is already closed at this point, so only the status code is available.

Solutions

  1. Log the status code and retry later if 429/5xx; back off the request rate.
  2. Handle 403 by rotating headers/User-Agent, cookies, or using a proxy/residential IP.
  3. Verify the search URL path is still valid if you get 404 (site layout changed).
  4. Optionally treat the body of non-200 responses as diagnostic info before discarding it.

Example fix

// before
if resp.StatusCode != 200 {
    return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// after
if resp.StatusCode != 200 {
    if resp.StatusCode == http.StatusTooManyRequests {
        time.Sleep(retryAfterFrom(resp.Header))
    }
    io.Copy(io.Discard, io.LimitReader(resp.Body, 4096))
    return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
Defensive patterns

Strategy: retry

Validate before calling

// after response
if resp.StatusCode != 200 { return fmt.Errorf("upstream status %d", resp.StatusCode) }

Try / catch

var statusErr *StatusError
if errors.As(err, &statusErr) {
    switch {
    case statusErr.Code == 429: backoffAndRetry()
    case statusErr.Code >= 500: retryLater()
    default: alertOnBlocked(statusErr.Code)
    }
}

Prevention

When it happens

Trigger: After doRequestWithRetry succeeds, resp.StatusCode != 200 — e.g. 403 (blocked/Cloudflare), 429 (rate limited), 404 (path changed), 5xx (server error) — ash.go:99.

Common situations: Upstream added WAF/anti-bot protection returning 403; request rate too high causing 429; the site changed its search path causing 404; origin server 5xx during load.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/6cb1f526f5825405. Report an issue: GitHub.

Appendix: source

Thrown at plugin/ash/ash.go:99

	// 创建请求
	req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
	}
	
	// 设置请求头
	p.setRequestHeaders(req)
	
	// 发送请求(优化重试)
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()
	
	// 检查状态码
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
	}
	
	// 读取响应(使用有限制的读取,避免读取过大内容)
	// ASH页面通常不会太大,限制在2MB以内
	limitReader := io.LimitReader(resp.Body, 2*1024*1024)
	body, err := io.ReadAll(limitReader)
	if err != nil {
		return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
	}
	
	// 从HTML中提取JSON数据(直接传递字节,避免字符串转换)
	results, err := p.extractResultsFromBytes(body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 提取搜索结果失败: %w", p.Name(), err)
	}
	
	// 关键词过滤
	filtered := plugin.FilterResultsByKeyword(results, keyword)

View on GitHub (pinned to beaa561337)