fish2018/pansou · error

[ ] 网盘第 页读取响应失败

Error message

[%s] %s网盘第%d页读取响应失败: %w

What it means

fetchSearchPage reads the whole response body with io.ReadAll and wraps any read failure with this message naming the plugin, pan type, and page. This happens when the connection breaks mid-body — the server closed the connection early, a keep-alive socket died, or a proxy interrupted the transfer.

Solutions

  1. Retry the request — the failure is usually transient; the retry wrapper handles the send but not the body read, so add a read-level retry.
  2. Disable keep-alive reuse (set req.Close = true or a fresh Transport) to avoid stale-connection read failures.
  3. Check network stability/proxy health between the host and the site.
  4. Set sensible limits with io.LimitReader if huge bodies are a concern.

Example fix

// before
body, err := io.ReadAll(resp.Body)
if err != nil {
    return nil, fmt.Errorf("...读取响应失败: %w", err)
}
// after
body, err := io.ReadAll(resp.Body)
if err != nil {
    time.Sleep(2 * time.Second)
    return p.fetchSearchPage(panType, keyword, pageNo) // retry once on transient read failure
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight connection stability
if _, err := http.Get("https://haisou.cc/"); err != nil {
    return fmt.Errorf("unstable connection to upstream: %w", err)
}

Type guard

func isBodyReadError(err error) bool { return err != nil && strings.Contains(err.Error(), "读取响应失败") }

Try / catch

items, err := fetchSearchPage(panType, keyword, pageNo)
if isBodyReadError(err) {
    time.Sleep(2 * time.Second)
    items, err = fetchSearchPage(panType, keyword, pageNo)
}

Prevention

When it happens

Trigger: Calling fetchSearchPage when io.ReadAll(resp.Body) errors: connection reset while streaming the body, server timeout mid-response, broken keep-alive connection reuse, or proxy truncating the response.

Common situations: Unstable network between the host and haisou.cc; server dropping long responses; HTTP client reusing a stale keep-alive connection; middlebox/proxy cutting idle or long connections.

Related errors


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

Appendix: source

Thrown at plugin/haisou/haisou.go:353

	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Referer", "https://haisou.cc/")

	// 发送HTTP请求(带重试机制)
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("[%s] %s网盘第%d页请求失败: %w", p.Name(), panType, pageNo, err)
	}
	defer resp.Body.Close()

	// 检查状态码
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("[%s] %s网盘第%d页返回状态码: %d", p.Name(), panType, pageNo, resp.StatusCode)
	}

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("[%s] %s网盘第%d页读取响应失败: %w", p.Name(), panType, pageNo, err)
	}

	// 解析响应
	var apiResp SearchAPIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("[%s] %s网盘第%d页JSON解析失败: %w", p.Name(), panType, pageNo, err)
	}

	// 检查API响应状态
	if apiResp.Code != 0 {
		return nil, fmt.Errorf("[%s] %s网盘第%d页API错误: %s", p.Name(), panType, pageNo, apiResp.Msg)
	}

	if DebugLog {
		fmt.Printf("[%s] %s网盘第%d页获取到 %d 个搜索结果\n", p.Name(), panType, pageNo, len(apiResp.Data.List))
	}

	return apiResp.Data.List, nil

View on GitHub (pinned to beaa561337)