fish2018/pansou · error
[ ] 读取搜索页面失败
Error message
[%s] 读取搜索页面失败: %w
What it means
This error means io.ReadAll on the search response body failed in the djgou plugin. It wraps the I/O error (e.g. unexpected EOF, connection reset mid-body). Rare, since the response headers already arrived, but it indicates the connection dropped while streaming the HTML.
Solutions
- Retry the search — this is usually transient; the plugin's retry loop does not cover body reading
- Lower request concurrency and disable aggressive keep-alive reuse if it recurs
- Check proxy/load balancer idle timeouts versus page generation time
- Capture the wrapped error to confirm reset vs EOF
Example fix
// before
items, err := p.searchImpl(k)
return err
// after
if err != nil && strings.Contains(err.Error(), "读取搜索页面失败") {
time.Sleep(2 * time.Second)
items, err = p.searchImpl(k)
} Defensive patterns
Strategy: retry
Validate before calling
// ensure network is up before calling
if _, err := net.LookupHost(hostOf(siteURL)); err != nil {
return fmt.Errorf("dns failure: %w", err)
} Try / catch
if err != nil && strings.Contains(err.Error(), "读取搜索页面失败") {
time.Sleep(2*time.Second)
items, err = p.searchImpl(keyword) // one manual retry
} Prevention
- Retry body-read failures; they are usually transient
- Avoid reusing keep-alive connections across long idle periods
- Keep proxy idle timeouts above page load time
- Lower concurrency against the site
When it happens
Trigger: io.ReadAll(resp.Body) returns err != nil in searchImpl — server closed the connection before sending the full body, or a proxy reset the stream.
Common situations: Unstable upstream/proxy connections; server-side timeouts on large pages; HTTP/2 GOAWAY mid-transfer; keep-alive connection reused after server timeout.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/02b2f002b5c76f6e.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/djgou/djgou.go:152
req.Header.Set("Cache-Control", "max-age=0")
req.Header.Set("Referer", SiteURL)
// 5. 发送请求(带重试机制)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
if resp.StatusCode != 200 {
resp.Body.Close()
return nil, fmt.Errorf("[%s] 搜索请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// 6. 读取并解析搜索结果页面。部分节点先返回 BTWAF JS 跳转页。
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("[%s] 读取搜索页面失败: %w", p.Name(), err)
}
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}
if doc.Find("article.post-item-row").Length() == 0 {
if match := btwafURLRegex.FindStringSubmatch(string(body)); len(match) > 1 {
challengeURL := match[1]
if strings.HasPrefix(challengeURL, "/") {
challengeURL = SiteURL + challengeURL
}
challengeReq, reqErr := http.NewRequestWithContext(ctx, http.MethodGet, challengeURL, nil)
if reqErr == nil {
challengeReq.Header = req.Header.Clone()
challengeResp, doErr := p.doRequestWithRetry(challengeReq, client)
if doErr == nil {
challengeBody, readErr := io.ReadAll(challengeResp.Body)
challengeResp.Body.Close()View on GitHub (pinned to beaa561337)