fish2018/pansou · error

[ ] 搜索请求失败

Error message

[%s] 搜索请求失败: %w

What it means

searchImpl wraps any error from p.doRequestWithRetry(req, client) — the actual GET round trip to ahhhhfs.com including its built-in retries — as '[%s] 搜索请求失败'. All transport-level failures (DNS, TCP, TLS, proxy errors, timeouts from DefaultTimeout, and retries exhausted) surface here. Because retries are already applied, an error here means the request failed persistently.

Solutions

  1. Read the wrapped %w error to distinguish timeout (context deadline exceeded) from connect/DNS failures.
  2. Verify reachability with curl -v 'https://www.ahhhhfs.com/' from the same host.
  3. If the site requires a proxy on this network, configure it on the http.Client's Transport.
  4. Increase DefaultTimeout if requests are consistently timing out near the limit.
  5. Check whether doRequestWithRetry's retry count/backoff is adequate; harden it against connection resets.

Example fix

// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return nil, fmt.Errorf("[%s] 搜索请求超时(>%s): %w", p.Name(), DefaultTimeout, err)
    }
    return nil, fmt.Errorf("[%s] 搜索请求失败(重试后仍失败): %w", p.Name(), err)
}
Defensive patterns

Strategy: retry

Try / catch

results, err := p.Search(ctx, keyword)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        // 超时: 增大 DefaultTimeout 或减少并发后重试
    } else {
        // 网络/代理故障: 换网络或配置代理后重试
    }
}

Prevention

When it happens

Trigger: doRequestWithRetry returns err after exhausting its retry attempts: DNS resolution failure for www.ahhhhfs.com, connection refused/reset, TLS handshake failure, context deadline exceeded (DefaultTimeout), or proxy misconfiguration — all persisting across retries.

Common situations: The site (or its CDN) blocks the host's IP or region; the machine has no/broken internet or DNS; DefaultTimeout is too short for the site's response time; a required proxy is not configured; the domain changed and the old one no longer resolves.

Related errors


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

Appendix: source

Thrown at plugin/ahhhhfs/ahhhhfs.go:181

	// 3. 创建请求
	req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
	}
	
	// 4. 设置完整的请求头(避免反爬虫)
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 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://www.ahhhhfs.com/")
	
	// 5. 发送请求(带重试机制)
	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)
	}
	
	// 6. 解析搜索结果页面
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
	}
	
	// 7. 提取搜索结果
	var results []model.SearchResult
	var wg sync.WaitGroup
	var mu sync.Mutex
	semaphore := make(chan struct{}, MaxConcurrency)

View on GitHub (pinned to beaa561337)