fish2018/pansou · error
请求失败
Error message
请求失败: %w
What it means
doSearch in the susu plugin wraps any error from doRequestWithRetry (the network layer performing the HTTP search request to BaseURL) with the message "请求失败" ("request failed"), preserving the underlying cause via %w. It is thrown whenever the retried HTTP search request ultimately fails — DNS failure, connection refused, TLS errors, timeouts, or retries exhausted. It signals the search never produced an HTTP response at all.
Solutions
- Check network connectivity to the susu site (curl the BaseURL from the same host) and confirm DNS resolves.
- Inspect the wrapped error (%w chain) with errors.Unwrap / %v to see the root cause (timeout vs connection refused vs TLS).
- Increase the HTTP client timeout and MaxRetries if the site is slow or intermittently failing.
- If the site blocks datacenter IPs, route through a working proxy or update BaseURL if the domain changed.
- For tests like TestSusuLiveSearch, ensure the test environment has internet access or run against a local mock server.
Example fix
// before
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
// after: unwrap and log root cause for easier debugging
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err) // use %v in logs to print the full chain
} Defensive patterns
Strategy: retry
Validate before calling
// check reachability before calling Search
conn, err := net.DialTimeout("tcp", "susu-host:443", 5*time.Second)
if err != nil { /* skip search, host unreachable */ }
conn.Close() Try / catch
links, err := p.Search(query)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// back off and retry later
}
log.Printf("susu search unavailable: %v", err)
return fallbackResults
} Prevention
- Verify outbound network/DNS in the deployment environment
- Configure sane client timeout and MaxRetries
- Monitor the upstream site's availability
- Provide a fallback search source when this plugin fails
When it happens
Trigger: Calling doSearch (directly or via the plugin's public Search, e.g. from TestSusuLiveSearch) when doRequestWithRetry exhausts MaxRetries without receiving a response: network unreachable, host resolution failure, TLS handshake failure, or client timeout on the search endpoint.
Common situations: Offline or firewalled environments; the susu site blocking the server's IP; DNS failures in containers; proxy misconfiguration; the target site being down or moving domains; MaxRetries being too low for a flaky network.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/1d13082b63a64dfd.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/susu/susu.go:161
searchURL := fmt.Sprintf(SearchURL, url.QueryEscape(keyword))
// 发送请求
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
// 设置请求头
req.Header.Set("User-Agent", getRandomUA())
setBrowserHeaders(req, BaseURL+"/")
// 发送请求(带重试)
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("[susu] 搜索请求返回状态码: %d", resp.StatusCode)
}
// 解析HTML
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("解析HTML失败: %w", err)
}
// 提取搜索结果
var wg sync.WaitGroup
resultChan := make(chan model.SearchResult, 20)
// 创建信号量控制并发数
semaphore := make(chan struct{}, MaxConcurrency)View on GitHub (pinned to beaa561337)