fish2018/pansou · error
[ ] 读取响应失败
Error message
[%s] 读取响应失败: %w
What it means
Thrown when io.ReadAll(resp.Body) fails after a successful HTTP response from the ouge API. The HTTP layer worked but the response body stream could not be fully read, typically because the connection was reset or closed mid-transfer. The plugin wraps the io error with its plugin name for identification.
Solutions
- Retry the search — the error is usually transient connection interruption
- Check network stability (VPN/proxy) between the client and woog.nxog.eu.org
- Verify the server is not closing connections early (curl -v the same endpoint)
- Add a bounded retry with backoff around searchImpl at the caller level
Example fix
// before
body, err := io.ReadAll(resp.Body)
if err != nil { return err }
// after
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize))
if err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) { return retrySearch() }
return err
} Defensive patterns
Strategy: retry
Try / catch
if err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET) {
return retryWithBackoff(search, 3, 500*time.Millisecond)
}
return err
} Prevention
- Use a stable wired connection or reliable proxy when scraping
- Bound body reads with io.LimitReader to avoid huge truncated transfers
- Retry transient io errors automatically
- Watch for repeated truncation signaling a middlebox problem
When it happens
Trigger: searchImpl reads resp.Body after doRequestWithRetry returned 200; the server closes the connection prematurely, a proxy truncates the stream, or the read times out mid-body (unexpected EOF / connection reset by peer).
Common situations: Flaky mobile/VPN connections; large result sets over an unstable link; server-side load balancer idle-timeout killing the connection; gzip/transfer encoding interrupted by a middlebox.
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/b5d136e7074f2efe.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/ouge/ouge.go:149
// 设置请求头
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", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Referer", "https://woog.nxog.eu.org/")
req.Header.Set("Cache-Control", "no-cache")
// 发送请求
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
// 解析JSON响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
var apiResponse OugeAPIResponse
if err := json.Unmarshal(body, &apiResponse); err != nil {
return nil, fmt.Errorf("[%s] 解析JSON响应失败: %w", p.Name(), err)
}
// 检查API响应状态
if apiResponse.Code != 1 {
return nil, fmt.Errorf("[%s] API返回错误: %s", p.Name(), apiResponse.Msg)
}
// 解析搜索结果
var results []model.SearchResult
for _, item := range apiResponse.List {
if result := p.parseAPIItem(item); result.Title != "" {
results = append(results, result)
}View on GitHub (pinned to beaa561337)