fish2018/pansou · error

[ ] 读取搜索结果失败

Error message

[%s] 读取搜索结果失败: %w

What it means

fetchSearch wraps an error from io.ReadAll(io.LimitReader(resp.Body, 4<<20)) — reading the response body failed mid-stream. The 4MB LimitReader caps memory use, so this indicates an actual I/O problem: the connection was reset or timed out while the body was being transferred, not an oversized page.

Solutions

  1. Check the wrapped %w error: 'context deadline exceeded' means requestTimeout elapsed during body read — increase requestTimeout.
  2. Add a retry around fetchSearch for transient connection-reset errors.
  3. If pages are legitimately near 4MB, raise the LimitReader cap (though most search pages are far smaller).
  4. Test with curl from the same host to see whether the full body downloads reliably.

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)
}
// 重新搜索时增加重试
var doc *goquery.Document
for i := 0; i < 3; i++ {
    if doc, err = p.fetchSearch(client, keyword); err == nil {
        break
    }
    time.Sleep(time.Second * time.Duration(i+1))
}
Defensive patterns

Strategy: retry

Try / catch

results, err := p.Search(ctx, keyword)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) || errors.Is(err, io.ErrUnexpectedEOF) {
        // 传输中断: 退避后重试
    }
}

Prevention

When it happens

Trigger: The TCP connection drops, the server closes early, or the request context's deadline expires while io.ReadAll is draining resp.Body on a large search results page.

Common situations: Unstable network or a flaky proxy interrupting large HTML transfers; the site slow-throttles responses and the context deadline (requestTimeout) cuts the read short; intermediaries (CDN/WAF) terminating long-running connections.

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/f4b288c9a7462601. Report an issue: GitHub.

Appendix: source

Thrown at plugin/5266ys/5266ys.go:190

	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)