fish2018/pansou · error
[ ] 读取搜索结果失败
Error message
[%s] 读取搜索结果失败: %w
What it means
Reading the response body via io.ReadAll(io.LimitReader(resp.Body, 4<<20)) failed, so the search results could not be loaded. This wraps the underlying read error (usually a network interruption or server closing the connection mid-body, surfaced as unexpected EOF or connection reset). The body is capped at 4 MiB to bound memory use.
Solutions
- Retry the search request — this is typically transient.
- Unwrap the error to distinguish io.ErrUnexpectedEOF / connection reset from other failures.
- If it recurs on large pages, check whether a proxy or timeout is cutting the transfer short.
- Consider reusing a shared http.Client with sane Transport timeouts and connection pooling.
Example fix
// before
body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
if err != nil {
return nil, fmt.Errorf("[%s] 读取搜索结果失败: %w", p.Name(), err)
}
// after
body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
if err != nil {
return nil, fmt.Errorf("[%s] 读取搜索结果失败(可重试): %w", p.Name(), err)
} Defensive patterns
Strategy: retry
Try / catch
if err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET) { /* transient: retry */ }
return nil, fmt.Errorf("[%s] 读取搜索结果失败: %w", p.Name(), err)
} Prevention
- Retry body-read failures — they are usually transient connection drops
- Configure the http.Client Transport with sane timeouts and keep-alives
- Keep the LimitReader cap so partial reads never exhaust memory
When it happens
Trigger: io.ReadAll returns an error while draining resp.Body: connection reset by peer, unexpected EOF, or any transport error occurring after headers were received but before the full body arrived.
Common situations: Flaky upstream connection, server timing out and dropping the connection mid-response, intermediate proxies killing long transfers, or mobile/unstable networks.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/7db58f541b89701a.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/dygang/dygang.go:189
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+searchPath, strings.NewReader(form))
if err != nil {
return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
}
setHeaders(req, baseURL+"/")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("[%s] 搜索请求返回 HTTP %d", p.Name(), resp.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
if err != nil {
return nil, fmt.Errorf("[%s] 读取搜索结果失败: %w", p.Name(), err)
}
decoded, err := decodeGB18030(body)
if err != nil {
return nil, fmt.Errorf("[%s] 解码搜索结果失败: %w", p.Name(), err)
}
doc, err := goquery.NewDocumentFromReader(strings.NewReader(decoded))
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
}
return doc, nil
}
func (p *Plugin) fetchDetail(client *http.Client, detailURL string) ([]magnetItem, string, string) {
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, detailURL, nil)
if err != nil {
return nil, "", ""View on GitHub (pinned to beaa561337)