fish2018/pansou · error
重试 次后失败
Error message
重试 %d 次后失败: %w
What it means
This error is returned by doRequestWithRetry after exhausting maxRetries attempts to fetch remote data. lastErr is wrapped with %w so the underlying cause (timeout, connection failure, non-2xx, etc.) is preserved. The plugin's callers fetchDataKey, fetchPosts, and fetchDetailLinks all surface it when the upstream source is unreachable or persistently failing.
Solutions
- Test connectivity to the upstream host from the deployment machine (curl the source URL) to confirm the network path works.
- Check the wrapped lastErr chain (errors.Unwrap / %v of the message) to identify the root cause — timeout vs status code vs DNS.
- Increase maxRetries or retryBaseDelay if the upstream is intermittently slow.
- Configure an HTTP proxy if the host requires one to be reachable.
- Verify the source URL is still valid; upstream APIs change or shut down over time.
Example fix
// before: retries fail with no diagnosis
result, err := fetchPosts(client)
// after: inspect the wrapped cause
if err != nil {
log.Printf("fetchPosts failed: %v", errors.Unwrap(err))
// e.g. context deadline exceeded -> raise requestTimeout or add proxy
} Defensive patterns
Strategy: retry
Validate before calling
url, err := url.Parse(sourceURL)
if err != nil || url == nil || url.Host == "" { return err }
conn, err := net.DialTimeout("tcp", net.JoinHostPort(url.Hostname(), portOr(url, "443")), 3*time.Second)
if err != nil { return fmt.Errorf("upstream unreachable: %w", err) }
conn.Close() Try / catch
results, err := fetchPosts(client)
if err != nil {
var retryErr = err
for unwrapped := errors.Unwrap(err); unwrapped != nil; unwrapped = errors.Unwrap(unwrapped) {
retryErr = unwrapped
}
log.Printf("upstream fetch failed after retries, root cause: %v", retryErr)
results = fallbackResults // serve cached/stale data
} Prevention
- Add health monitoring for upstream sources and alert before users hit failures.
- Cache successful responses so retries can fall back to stale data.
- Keep retry counts and delays configurable per environment.
- Log the fully unwrapped error chain, not just the wrapper message.
When it happens
Trigger: Any of fetchDataKey, fetchPosts, or fetchDetailLinks calling doRequestWithRetry while the remote endpoint keeps failing on every attempt (network errors, timeouts, repeated bad status codes), after exponential backoff sleeps of retryBaseDelay<<attempt between attempts.
Common situations: Upstream site is down or blocked (e.g. by GFW or geo-restriction), DNS failure in the deployment environment, no outbound internet in a container, or the source raising rate limits that persist across all retries.
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/38e7c1f178a94899.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/jsnoteclub/jsnoteclub.go:516
for attempt := 0; attempt < maxRetries; attempt++ {
resp, err := client.Do(req.Clone(req.Context()))
if err == nil && resp.StatusCode == http.StatusOK {
return resp, nil
}
if resp != nil {
resp.Body.Close()
}
lastErr = err
if err == nil {
lastErr = fmt.Errorf("HTTP 状态码 %d", resp.StatusCode)
}
if attempt < maxRetries-1 {
time.Sleep(retryBaseDelay * time.Duration(1<<attempt))
}
}
return nil, fmt.Errorf("重试 %d 次后失败: %w", maxRetries, lastErr)
}
func newHTTPClient() *http.Client {
return &http.Client{
Timeout: requestTimeout,
Transport: &http.Transport{
MaxIdleConns: httpMaxIdleConns,
MaxIdleConnsPerHost: httpMaxIdlePerHost,
MaxConnsPerHost: httpMaxConnsPerHost,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
},
}
}
func setHTMLHeaders(req *http.Request, referer string) {
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")View on GitHub (pinned to beaa561337)