fish2018/pansou · error
[ ] 读取响应体失败
Error message
[%s] 读取响应体失败: %w
What it means
searchImpl reads the entire response body with io.ReadAll after a 200 status. This error wraps a read failure, which happens when the connection is broken mid-body: server closes the connection prematurely, keep-alive idle connection was reused after it died, TLS error mid-stream, or a proxy truncates the response.
Solutions
- Log the wrapped %w error to confirm premature-close vs TLS issue
- Rely on doRequestWithRetry: ensure read errors also trigger a retry (retry before reading the body, or treat read failure as retryable)
- Disable keep-alive reuse issues by tuning the transport's IdleConnTimeout or forcing a fresh connection on retry
- Check for intermediary proxies/LBs with short response timeouts
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, maxResponseSize))
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应体失败(可重试): %w", p.Name(), err)
} Defensive patterns
Strategy: retry
Validate before calling
// declare expected size upfront and cap the read: body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
Try / catch
if err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET) {
// transient mid-body disconnect: safe to retry the whole request
return retrySearch()
}
return nil, err
} Prevention
- Tune Transport IdleConnTimeout below the server's keep-alive timeout to avoid stale reused connections
- Retry the full request when body reads fail — responses were not consumed
- Use LimitReader to bound memory and avoid gigantic truncated reads
- Check intermediary proxies for short response timeouts
When it happens
Trigger: io.ReadAll fails while draining resp.Body: unexpected EOF from a dropped connection, connection reset by peer mid-transfer, or TLS record errors on a reused connection.
Common situations: Flaky mobile/proxy networks; server or load balancer timeouts cutting large responses; aggressive keep-alive reuse hitting a server-side idle timeout; unstable container networking.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/6b485905a16ea4fe.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/meitizy/meitizy.go:185
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Origin", FrontendURL)
req.Header.Set("Referer", FrontendURL+"/")
// 使用优化的客户端发送请求(带重试)
resp, err := p.doRequestWithRetry(req, p.optimizedClient)
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] 搜索请求HTTP状态错误: %d", p.Name(), resp.StatusCode)
}
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应体失败: %w", p.Name(), err)
}
// 解析JSON响应
var apiResp searchResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
return nil, fmt.Errorf("[%s] JSON解析失败: %w", p.Name(), err)
}
// 转换为标准格式
results := p.convertToSearchResults(apiResp.Data)
// 关键词过滤(标准网盘插件需要过滤)
filteredResults := plugin.FilterResultsByKeyword(results, keyword)
return filteredResults, nil
}
// convertToSearchResults 将API响应转换为标准SearchResult格式View on GitHub (pinned to beaa561337)