fish2018/pansou · error

请求失败

Error message

请求失败: %w

What it means

doSearch in the susu plugin wraps any error from doRequestWithRetry (the network layer performing the HTTP search request to BaseURL) with the message "请求失败" ("request failed"), preserving the underlying cause via %w. It is thrown whenever the retried HTTP search request ultimately fails — DNS failure, connection refused, TLS errors, timeouts, or retries exhausted. It signals the search never produced an HTTP response at all.

Solutions

  1. Check network connectivity to the susu site (curl the BaseURL from the same host) and confirm DNS resolves.
  2. Inspect the wrapped error (%w chain) with errors.Unwrap / %v to see the root cause (timeout vs connection refused vs TLS).
  3. Increase the HTTP client timeout and MaxRetries if the site is slow or intermittently failing.
  4. If the site blocks datacenter IPs, route through a working proxy or update BaseURL if the domain changed.
  5. For tests like TestSusuLiveSearch, ensure the test environment has internet access or run against a local mock server.

Example fix

// before
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil {
    return nil, fmt.Errorf("请求失败: %w", err)
}
// after: unwrap and log root cause for easier debugging
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil {
    return nil, fmt.Errorf("请求失败: %w", err) // use %v in logs to print the full chain
}
Defensive patterns

Strategy: retry

Validate before calling

// check reachability before calling Search
conn, err := net.DialTimeout("tcp", "susu-host:443", 5*time.Second)
if err != nil { /* skip search, host unreachable */ }
conn.Close()

Try / catch

links, err := p.Search(query)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) && netErr.Timeout() {
        // back off and retry later
    }
    log.Printf("susu search unavailable: %v", err)
    return fallbackResults
}

Prevention

When it happens

Trigger: Calling doSearch (directly or via the plugin's public Search, e.g. from TestSusuLiveSearch) when doRequestWithRetry exhausts MaxRetries without receiving a response: network unreachable, host resolution failure, TLS handshake failure, or client timeout on the search endpoint.

Common situations: Offline or firewalled environments; the susu site blocking the server's IP; DNS failures in containers; proxy misconfiguration; the target site being down or moving domains; MaxRetries being too low for a flaky network.

Related errors


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

Appendix: source

Thrown at plugin/susu/susu.go:161

	searchURL := fmt.Sprintf(SearchURL, url.QueryEscape(keyword))

	// 发送请求
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("创建请求失败: %w", err)
	}

	// 设置请求头
	req.Header.Set("User-Agent", getRandomUA())
	setBrowserHeaders(req, BaseURL+"/")

	// 发送请求(带重试)
	resp, err := p.doRequestWithRetry(client, req, MaxRetries)
	if err != nil {
		return nil, fmt.Errorf("请求失败: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("[susu] 搜索请求返回状态码: %d", resp.StatusCode)
	}

	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("解析HTML失败: %w", err)
	}

	// 提取搜索结果
	var wg sync.WaitGroup
	resultChan := make(chan model.SearchResult, 20)

	// 创建信号量控制并发数
	semaphore := make(chan struct{}, MaxConcurrency)

View on GitHub (pinned to beaa561337)