fish2018/pansou · error
重试 次后仍然失败
Error message
重试 %d 次后仍然失败: %w
What it means
doRequestWithRetry exhausts maxRetries attempts without a successful response and returns "重试 %d 次后仍然失败" wrapping the last error (either a transport error or the synthesized "HTTP 状态码 %d"). It signals the caller that the request failed permanently after all retries.
Solutions
- Unwrap the cause (errors.Unwrap) to distinguish transport failure from a bad HTTP status.
- Increase maxRetries and add exponential backoff with jitter between attempts.
- Verify reachability of the target host (curl/DNS) and check for proxy/firewall blocking.
- If 403/429 persist, rotate User-Agent/cookies or respect rate limits before retrying.
- Surface the error to the user as 'source temporarily unavailable' and allow the caller to retry later.
Example fix
// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, err
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("source unavailable after retries: %w", err) // caller can retry later
} Defensive patterns
Strategy: retry
Try / catch
results, err := plugin.Search(ctx, keyword)
if err != nil {
if strings.Contains(err.Error(), "重试") {
// all retries exhausted — schedule a later retry with backoff
return nil, fmt.Errorf("source temporarily unavailable: %w", err)
}
return nil, err
} Prevention
- Configure sufficient retries with exponential backoff plus jitter.
- Check host reachability (DNS, firewall, proxy) before blaming the plugin.
- Rate-limit your own requests to avoid triggering site-side bans that outlast the retry window.
When it happens
Trigger: fetchSearchResults or getDownloadLinks issues a request that fails on every attempt: persistent network outage, site hard-blocking the client, sustained 5xx, or retries exhausted faster than the outage lasted.
Common situations: Site is down for maintenance, IP rate-limited/banned across the whole retry window, corporate proxy blocking the domain, or maxRetries too low for a flaky connection.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/c24ee763fec2ac67.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/cyg/cyg.go:399
// 克隆请求避免并发问题
reqClone := req.Clone(req.Context())
resp, err := client.Do(reqClone)
if err == nil && resp.StatusCode == 200 {
return resp, nil
}
if resp != nil {
resp.Body.Close()
}
lastErr = err
if err == nil {
lastErr = fmt.Errorf("HTTP 状态码 %d", resp.StatusCode)
}
}
return nil, fmt.Errorf("重试 %d 次后仍然失败: %w", maxRetries, lastErr)
}
// parseExtOptions 从ext参数中解析搜索选项
func (p *CygPlugin) parseExtOptions(ext map[string]interface{}) CygSearchOptions {
opts := CygSearchOptions{
PerPage: 20,
Page: 1,
OrderBy: "date",
Order: "desc",
}
if ext == nil {
return opts
}
if perPage, ok := ext["per_page"].(int); ok && perPage > 0 {
opts.PerPage = perPage
}View on GitHub (pinned to beaa561337)