fish2018/pansou · error
[ ] 搜索请求失败
Error message
[%s] 搜索请求失败: %w
What it means
fetchSearchResults wraps the error returned by doRequestWithRetry after the request creation succeeded. doRequestWithRetry already retried the request maxRetries times, so this error means the kkv search page could not be fetched at all — network failure, timeout, TLS error, or the server refused/dropped the connection on every attempt.
Solutions
- Verify the kkv base URL resolves and is reachable from the deployment machine (curl the search URL there)
- Check the wrapped error: if it is a context deadline, consider raising the 30s timeout; if connection refused, the domain is wrong or dead
- Use a proxy or mirror domain if the source is network-blocked at the deployment location
- Confirm the app has outbound internet/DNS access (containers often lack it)
Example fix
// before ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // after ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // slow/blocked upstream
Defensive patterns
Strategy: retry
Validate before calling
conn, err := net.DialTimeout("tcp", host+":443", 5*time.Second)
if err != nil {
// upstream unreachable before even calling Search
} Type guard
func isTimeoutErr(err error) bool {
var ne net.Error
return errors.As(err, &ne) && ne.Timeout() || errors.Is(err, context.DeadlineExceeded)
} Try / catch
results, err := plugin.Search(ctx, kw)
if err != nil {
if isTimeoutErr(err) {
ctx2, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
return plugin.Search(ctx2, kw)
}
log.Printf("search transport failed: %v", err)
return nil
} Prevention
- Check outbound internet/DNS from the deployment environment first
- Pre-probe source availability with a cheap HEAD request before batch searches
- Keep timeouts generous for slow overseas sources
- Configure a proxy when sources are geo-blocked
When it happens
Trigger: p.doRequestWithRetry(req, client) returns err after exhausting retries: DNS failure for the kkv domain, 30s context timeout, TLS handshake failure, or connection refused/reset each time.
Common situations: The kkv site is blocked or unreachable from the server's network (geo-block, GFW), DNS resolution fails for a dead mirror, the server has no outbound internet, or the 30-second timeout expires on a slow site.
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/842fa23fedc82a2d.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/kkv/kkv.go:130
return filtered
}
func (p *KKVPlugin) fetchSearchResults(searchURL string, client *http.Client) ([]searchItem, error) {
debugPrintf("🌐 请求搜索页面: %s\n", searchURL)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
p.setHeaders(req, baseURL)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
debugPrintf("📡 HTTP状态码: %d\n", resp.StatusCode)
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
var items []searchItem
doc.Find("article.post").Each(func(i int, s *goquery.Selection) {
link := s.Find(".entry-header h2.entry-title a")
href, exists := link.Attr("href")View on GitHub (pinned to beaa561337)