fish2018/pansou · error

重试 次后失败

Error message

重试 %d 次后失败: %w

What it means

doJuPansouRequestWithRetry returns this after jupansouMaxRetries attempts all failed, wrapping lastErr (either the last transport error or the last non-2xx status error). It is the terminal failure of the HTTP fetch step for the JuPansou search and propagates to searchImpl's "请求失败" wrapper.

Solutions

  1. Unwrap the chain to see the last attempt's cause (errors.Unwrap twice: retry wrapper → status/transport error).
  2. Test the endpoint manually with curl from the same host.
  3. Increase jupansouMaxRetries or the backoff base if the outage is brief.
  4. Apply exponential backoff with jitter and circuit-breaking to avoid hammering a downed upstream.
  5. Check for IP-level bans; rotate egress IP or use a proxy.

Example fix

// before: fixed retry count, no jitter
time.Sleep(200 * time.Millisecond * time.Duration(1<<attempt))
// after: add jitter to avoid thundering-hered retries
time.Sleep(time.Duration(float64(200*time.Millisecond) * (1 << attempt) * (0.5 + rand.Float64())))
Defensive patterns

Strategy: retry

Try / catch

results, err := p.searchImpl(client, keyword)
if err != nil && strings.Contains(err.Error(), "重试") {
    log.Printf("jupansou exhausted retries: %v", err)
    results = cache.Get(keyword) // stale fallback
}

Prevention

When it happens

Trigger: Every retry attempt in the loop failed — persistent connection errors, timeouts, or repeated non-2xx status codes — with exponential backoff (200ms<<attempt) between attempts, until the loop ends.

Common situations: Sustained upstream outage; IP blocked or rate-limited for the entire retry window; DNS failures in the deployment environment; timeout too short for every attempt.

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

Appendix: source

Thrown at plugin/jupansou/jupansou.go:340

func doJuPansouRequestWithRetry(req *http.Request, client *http.Client) (*http.Response, error) {
	var lastErr error
	for attempt := 0; attempt < jupansouMaxRetries; 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 lastErr == nil {
			lastErr = fmt.Errorf("HTTP 状态码 %d", resp.StatusCode)
		}
		if attempt < jupansouMaxRetries-1 {
			time.Sleep(200 * time.Millisecond * time.Duration(1<<attempt))
		}
	}
	return nil, fmt.Errorf("重试 %d 次后失败: %w", jupansouMaxRetries, lastErr)
}

View on GitHub (pinned to beaa561337)