fish2018/pansou · error
[ ] 读取响应失败
Error message
[%s] 读取响应失败: %w
What it means
CldiPlugin.searchPage wraps io.ReadAll(resp.Body) failures with the plugin name. The response arrived but its body could not be read to completion — usually the connection was reset mid-transfer or the body was truncated (unexpected EOF). It is a one-shot read here: no retry is attempted after this point.
Solutions
- Retry the search; transient resets often succeed on a second attempt.
- Check for intermediary proxies/LBs with short idle timeouts and raise them.
- Increase client timeouts and use a bounded reader to avoid oversized bodies.
- Verify network stability between client and the site.
Defensive patterns
Strategy: retry
Try / catch
results, err := plugin.Search(ctx, keyword)
if err != nil {
if strings.Contains(err.Error(), "读取响应失败") {
// transient: retry once after short backoff, then degrade to cached results
}
} Prevention
- Retry transient mid-body failures with backoff
- Keep client timeouts comfortably above the site's p95 response time
- Check intermediary proxy/LB timeouts that cut large transfers
- Prefer a bounded reader with a sane max body size
When it happens
Trigger: io.ReadAll(resp.Body) returns err in searchPage, typically when the server or an intermediary closes the connection before the full HTML body is delivered.
Common situations: Large search-result pages over unstable connections, load balancer idle timeouts cutting the transfer, proxy interference, or server-side crashes mid-response.
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/ce0721d2630a367e.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/cldi/cldi.go:157
// 设置请求头
p.setRequestHeaders(req)
// 发送请求
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
}
// setRequestHeaders 设置请求头
func (p *CldiPlugin) setRequestHeaders(req *http.Request) {
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")View on GitHub (pinned to beaa561337)