fish2018/pansou · error
[ ] 搜索请求失败
Error message
[%s] 搜索请求失败: %w
What it means
searchImpl wraps any error returned by doRequestWithRetry — the HTTP GET to the duoduo search page failed after all configured retry attempts. This covers transport-level failures: DNS resolution, TCP connect, TLS handshake, context deadline exceeded, and proxy errors. The underlying net.Error / context error is preserved via %w.
Solutions
- Test connectivity to the site directly (curl -v the search URL) to confirm it is reachable.
- Increase DefaultTimeout in the plugin or the retry count in doRequestWithRetry if the site is slow.
- Check for required proxy settings; if the site needs one, configure the http.Client transport with a proxy.
- Handle the wrapped error with errors.As for *net.OpError / context.DeadlineExceeded and surface a clear message or fall back to another plugin.
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] 搜索超时,站点不可达或超时过短: %w", p.Name(), err)
}
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
} Defensive patterns
Strategy: retry
Validate before calling
func canReach(urlStr string) bool {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodHead, urlStr, nil)
if err != nil { return false }
resp, err := http.DefaultClient.Do(req)
if err != nil { return false }
resp.Body.Close()
return true
} Try / catch
if err != nil {
var netErr net.Error
switch {
case errors.Is(err, context.DeadlineExceeded):
// treat as timeout: lengthen timeout or skip source
case errors.As(err, &netErr):
// transient network issue: retry with backoff
default:
// unexpected: log and fall back to another plugin
}
} Prevention
- Configure sane timeout and retry counts for slow upstreams.
- Check site reachability before enabling the plugin.
- Support proxy configuration for geo-blocked sources.
- Fall back to alternate search sources on persistent failure.
When it happens
Trigger: doRequestWithRetry(req, client) exhausts its retry loop because every attempt returns a non-nil error (server unreachable, DNS failure, TLS errors, or the DefaultTimeout context expired).
Common situations: Target site tv.yydsys.top is down or blocked; developer is behind a firewall/proxy; DNS cannot resolve the domain; network is slow enough that DefaultTimeout expires on every attempt; running from a region where the site is geo-blocked.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/8a13921f14c52491.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/duoduo/duoduo.go:179
// 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/91.0.4472.124 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://tv.yydsys.top/")
// 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
doc.Find(".module-search-item").Each(func(i int, s *goquery.Selection) {
result := p.parseSearchItem(s, keyword)View on GitHub (pinned to beaa561337)