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

  1. Retry the search request — this is typically transient.
  2. Unwrap the error to distinguish io.ErrUnexpectedEOF / connection reset from other failures.
  3. If it recurs on large pages, check whether a proxy or timeout is cutting the transfer short.
  4. 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

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)