fish2018/pansou · error
[ ] 读取响应失败
Error message
[%s] 读取响应失败: %w
What it means
searchPage wraps the error from io.ReadAll(resp.Body): the response body could not be fully read from the network connection. Because defer resp.Body.Close() runs right after, this typically means the connection dropped mid-transfer (unexpected EOF, connection reset by peer) or the compressed/encoded stream was corrupt.
Solutions
- Check the wrapped error (unexpected EOF vs context deadline vs connection reset)
- Retry the search — transient truncation often succeeds on a second attempt
- Increase MaxRetries in doRequestWithRetry to cover mid-body drops
- Increase TimeoutSeconds if the deadline is being hit on large pages
- Ensure no misconfigured proxy is intercepting and truncating traffic
Defensive patterns
Strategy: retry
Try / catch
results, err := p.Search(keyword, ext)
if err != nil && strings.Contains(err.Error(), "读取响应失败") {
// transient body read failure: retry with backoff
time.Sleep(time.Second)
results, err = p.Search(keyword, ext)
} Prevention
- Enable the plugin's built-in retry (MaxRetries) for transient truncation
- Increase TimeoutSeconds on slow links so large pages finish downloading
- Avoid unreliable proxies that terminate connections mid-body
- Retry once automatically — these failures are usually transient
When it happens
Trigger: io.ReadAll(resp.Body) fails after a 200 response: server closed the connection mid-body, TLS truncation, proxy interference, or an interrupted read hitting the 30s context deadline.
Common situations: Flaky upstream site or CDN dropping large HTML pages; overloaded server truncating responses; NAT/firewall killing long-lived connections; unstable network on the host running pansou.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/5351e7935ba2fb61.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/wuji/wuji.go:203
// 设置请求头
p.setRequestHeaders(req)
// 发送HTTP请求
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)
}
// 读取响应体内容
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
// 解析HTML
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
// 提取搜索结果
return p.extractSearchResults(doc), nil
}
// extractSearchResults 提取搜索结果
func (p *WujiPlugin) extractSearchResults(doc *goquery.Document) []model.SearchResult {
var results []model.SearchResult
// 查找所有搜索结果
doc.Find("table.file-list tbody tr").Each(func(i int, s *goquery.Selection) {View on GitHub (pinned to beaa561337)