fish2018/pansou · error

[ ] 搜索请求失败

Error message

[%s] 搜索请求失败: %w

What it means

CldiPlugin.searchPage wraps errors from doRequestWithRetry with the plugin name. This covers transport-level failures of the search request after internal retries: DNS failure, connection refused/reset, TLS errors, or context deadline exceeded (30s timeout). The underlying cause is preserved via %w.

Solutions

  1. Unwrap with errors.Is(err, context.DeadlineExceeded) to distinguish timeout from connection failure.
  2. Test reachability of the search URL with curl and the same headers.
  3. Increase the 30s timeout if the site is slow, or add proxy rotation if blocked.
  4. Retry with backoff; check for rate limiting or IP bans.

Example fix

// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return nil, fmt.Errorf("[%s] 搜索请求超时(30s): %w", p.Name(), err)
    }
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight reachability
conn, err := net.DialTimeout("tcp", host+":443", 5*time.Second)
if err != nil { log.Printf("搜索站点不可达: %v", err) } else { conn.Close() }

Try / catch

results, err := plugin.Search(ctx, keyword)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        // timeout: retry with backoff or raise the 30s limit
    } else if strings.Contains(err.Error(), "搜索请求失败") {
        // transport failure: check network/proxy, consider fallback plugin
    }
}

Prevention

When it happens

Trigger: p.doRequestWithRetry(req, client) returns err in searchPage — network unreachable, server down, or the 30-second context timeout elapsed before a response.

Common situations: Target site blocking the client's IP, firewall/proxy interference, slow site exceeding the 30s timeout, or transient network outages.

Related errors


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

Appendix: source

Thrown at plugin/cldi/cldi.go:145

	searchURL := fmt.Sprintf("%s/search-%s-0-2-%d.html", baseURL, url.QueryEscape(keyword), page)

	// 创建带超时的上下文
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

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

	// 设置请求头
	p.setRequestHeaders(req)

	// 发送请求
	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 != 200 {
		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)
	}

	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
	if err != nil {
		return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)

View on GitHub (pinned to beaa561337)