fish2018/pansou · error

[ ] 网盘第 页请求失败

Error message

[%s] %s网盘第%d页请求失败: %w

What it means

fetchSearchPage sends the search request through p.doRequestWithRetry and wraps any transport-level failure with this message including plugin name, pan type, and page. The wrapped %w error carries the underlying cause — DNS failure, connection refused/reset, TLS error, or exhausting the retry attempts.

Solutions

  1. Read the wrapped error (%w cause) to distinguish timeout vs connection-refused vs TLS.
  2. Check basic connectivity: curl https://haisou.cc/ from the same host.
  3. Increase the 30s context timeout if responses are merely slow.
  4. If the site blocks you, change egress IP or add delays/reduce concurrency.

Example fix

// before
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
// after
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
    if _, err = fetchPage(ctx, pageNo); err == nil { break }
    lastErr = err
    time.Sleep(time.Duration(attempt+1) * 2 * time.Second)
}
Defensive patterns

Strategy: retry

Validate before calling

// verify egress before issuing the search
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, http.MethodHead, "https://haisou.cc/", nil)
if _, err := http.DefaultClient.Do(req); err != nil {
    return fmt.Errorf("network unavailable: %w", err)
}

Type guard

func isNetworkError(err error) bool {
  var ne net.Error
  return errors.As(err, &ne) || errors.Is(err, context.DeadlineExceeded)
}

Try / catch

items, err := fetchSearchPage(panType, keyword, pageNo)
if isNetworkError(err) {
    time.Sleep(time.Duration(attempt) * 2 * time.Second) // exponential backoff
    items, err = fetchSearchPage(panType, keyword, pageNo)
}

Prevention

When it happens

Trigger: Calling fetchSearchPage when the HTTP round-trip fails even after retries: haisou.cc unreachable, connection reset/blocked, TLS handshake failure, context deadline exceeded (30s timeout), or the client's proxy is down.

Common situations: Deployment host without internet/egress; site blocking the IP at TCP/TLS level; haisou.cc DNS records changed; transient network outage during retries; timeout on slow responses.

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

Appendix: source

Thrown at plugin/haisou/haisou.go:341

	defer cancel()

	// 创建请求对象
	req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] %s网盘第%d页创建请求失败: %w", p.Name(), panType, pageNo, err)
	}

	// 设置请求头
	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://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)

View on GitHub (pinned to beaa561337)