fish2018/pansou · error

[ ] 读取响应失败

Error message

[%s] 读取响应失败: %w

What it means

ikantv plugin's doSearch throws this when io.ReadAll(resp.Body) fails after a 200 response. The response body could not be read to completion — the connection dropped mid-transfer or a transport-level read failed.

Solutions

  1. Retry the request; a mid-body drop is typically transient.
  2. Review the HTTP client's Transport timeouts (ResponseHeaderTimeout vs overall context) to ensure the body has time to transfer.
  3. Disable connection reuse if stale keep-alive connections repeatedly reset.
  4. Treat repeated occurrences as an upstream/network stability problem and log the wrapped io error.

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, maxBodySize))
if err != nil {
	return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
if len(body) == 0 {
	return nil, fmt.Errorf("[%s] 响应体为空", p.Name())
}
Defensive patterns

Strategy: retry

Try / catch

results, err := p.doSearch(ctx, keyword)
if err != nil && strings.Contains(err.Error(), "读取响应失败") {
	// transient body read failure: retry once after a short backoff
	time.Sleep(500 * time.Millisecond)
	results, err = p.doSearch(ctx, keyword)
}

Prevention

When it happens

Trigger: io.ReadAll(resp.Body) returns err on a 200 response: unexpected EOF from an abruptly closed connection, reset by peer, or read deadline hit while streaming the body.

Common situations: Upstream or CDN closes keep-alive connections prematurely; large responses interrupted by an unstable network; client Transport ReadTimeout shorter than the body transfer.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/246535152f83b132. Report an issue: GitHub.

Appendix: source

Thrown at plugin/ikantv/ikantv.go:95

	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", defaultReferer)

	resp, err := p.doRequestWithRetry(req, client)
	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] 请求返回状态码: %d", p.Name(), resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
	}

	var apiResp apiResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("[%s] JSON解析失败: %w", p.Name(), err)
	}
	if apiResp.Code != 0 {
		return nil, fmt.Errorf("[%s] API错误: %s", p.Name(), apiResp.Message)
	}

	results := convertResults(apiResp.Data)
	return plugin.FilterResultsByKeyword(results, keyword), nil
}

func convertResults(items []apiItem) []model.SearchResult {
	results := make([]model.SearchResult, 0, len(items))
	for _, item := range items {
		result, ok := convertResult(item)

View on GitHub (pinned to beaa561337)