fish2018/pansou · error
[ ] 请求失败,重试 次后仍失败
Error message
[%s] 请求失败,重试%d次后仍失败: %w
What it means
doRequestWithRetry in the Erxiao plugin retries an outgoing HTTP search request up to maxRetries times with short 100ms backoff and, if every attempt fails, wraps the last underlying error in this message. It signals that the upstream site could not be reached or did not answer successfully across all quick retries.
Solutions
- Inspect the wrapped %w cause to identify the concrete network error
- Verify network/DNS connectivity to the erxiao upstream host
- Increase maxRetries or the 100ms sleep backoff for slow upstreams
- Add proxy support if the host is blocked from this environment
Example fix
// before
return nil, fmt.Errorf("[%s] 请求失败,重试%d次后仍失败: %w", p.Name(), maxRetries, lastErr)
// after
backoff := 100 * time.Millisecond
for i := 0; i < maxRetries; i++ {
resp, err = client.Do(req)
if err == nil { break }
time.Sleep(backoff)
backoff *= 2
} Defensive patterns
Strategy: retry
Validate before calling
// pre-check upstream reachability
resp, err := http.Head("https://erxiao-upstream-host/")
if err != nil || resp.StatusCode >= 500 { /* skip plugin this round */ } Try / catch
results, err := plugin.Search(keyword)
if err != nil {
var retriable bool
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, syscall.ECONNRESET) { retriable = true }
log.Printf("erxiao unavailable (retriable=%v): %v", retriable, err)
results = nil // degrade gracefully to other plugins
} Prevention
- Aggregate results from multiple plugins so one failing plugin degrades gracefully
- Monitor the wrapped cause of retry-exhausted errors to detect upstream outages early
- Use exponential backoff instead of fixed 100ms
- Set sane per-request timeouts shorter than the overall context deadline
When it happens
Trigger: All attempts inside doRequestWithRetry return non-nil errors (network failure, timeout, connection reset) and searchAtBase or fetchDetailLinksAndImages surfaces the wrapped lastErr.
Common situations: Upstream site blocked or geo-restricted, DNS failures, server under load or rate-limiting, local network outage, or 100ms backoff too short for a slow upstream.
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/c08df5662bdd8f83.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/erxiao/erxiao.go:554
if resp.StatusCode == http.StatusOK {
return resp, nil
}
resp.Body.Close()
lastErr = fmt.Errorf("HTTP状态码: %d", resp.StatusCode)
} else {
lastErr = err
}
if req.Context().Err() != nil {
return nil, req.Context().Err()
}
// 快速重试:只等待很短时间
if i < maxRetries-1 {
time.Sleep(100 * time.Millisecond)
}
}
return nil, fmt.Errorf("[%s] 请求失败,重试%d次后仍失败: %w", p.Name(), maxRetries, lastErr)
}
// GetPerformanceStats 获取性能统计信息
func (p *ErxiaoAsyncPlugin) GetPerformanceStats() map[string]interface{} {
totalRequests := atomic.LoadInt64(&searchRequests)
totalTime := atomic.LoadInt64(&totalSearchTime)
detailRequests := atomic.LoadInt64(&detailPageRequests)
detailTime := atomic.LoadInt64(&totalDetailTime)
hits := atomic.LoadInt64(&cacheHits)
misses := atomic.LoadInt64(&cacheMisses)
var avgTime float64
if totalRequests > 0 {
avgTime = float64(totalTime) / float64(totalRequests) / 1e6 // 转换为毫秒
}
var avgDetailTime float64
if detailRequests > 0 {View on GitHub (pinned to beaa561337)