fish2018/pansou · error
[ ] 读取响应失败
Error message
[%s] 读取响应失败: %w
What it means
ClmaoPlugin.searchPage fails while reading the HTTP response body with io.ReadAll. Since the status already passed the 200 check, this is almost always a mid-stream network failure (connection reset, unexpected EOF) or the request context expiring while the body is still streaming.
Solutions
- Check the wrapped error: context.DeadlineExceeded means the timeout hit during body read — increase TimeoutSeconds.
- Retry the request; mid-stream resets are often transient and re-entering searchPage reuses doRequestWithRetry.
- Use io.LimitReader to bound body size and avoid pathological huge reads.
- Ensure resp.Body is fully drained/closed promptly so keep-alive connections aren't broken.
Example fix
// before
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
// after
body, err := io.ReadAll(io.LimitReader(resp.Body, 10<<20))
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return nil, fmt.Errorf("[%s] 读取响应超时,尝试增大 TimeoutSeconds: %w", p.Name(), err)
}
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
} Defensive patterns
Strategy: retry
Try / catch
body, err := io.ReadAll(resp.Body)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.ErrUnexpectedEOF) {
return retryOnce(req) // transient mid-stream failure
}
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
} Prevention
- Set TimeoutSeconds high enough to cover full body download, not just headers.
- Bound response size with io.LimitReader to avoid pathological reads.
- Treat mid-stream resets as transient and retry.
- Drain and close response bodies promptly to keep connections reusable.
When it happens
Trigger: After a 200 response, body, err := io.ReadAll(resp.Body) errors — the connection dropped mid-transfer, the server truncated the response, or ctx (TimeoutSeconds) expired during body read — producing "[clmao] 读取响应失败: %w".
Common situations: Slow/large responses exceeding the TimeoutSeconds budget (deadline hits while reading); flaky networks or proxies closing connections; anti-bot middleboxes resetting the connection after headers were sent.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/466e7a52135ff995.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/clmao/clmao.go:200
// 设置请求头
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)
}
decodedHTML := decodeModernPayload(string(body))
if decodedHTML != string(body) {
if modernResults := p.parseModernSearchResults(client, decodedHTML); len(modernResults) > 0 {
return modernResults, nil
}
}
// 兼容旧模板
doc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
// 提取搜索结果
return p.extractSearchResults(doc), nil
}View on GitHub (pinned to beaa561337)