fish2018/pansou · error
[ ] 重试 次后仍失败
Error message
[%s] 重试 %d 次后仍失败: %w
What it means
doRequestWithRetry wraps the final failure as '[<plugin>] 重试 %d 次后仍失败: %w' after exhausting maxRetries=3 with exponential backoff (200ms, 400ms). It aggregates whatever error (transport error or the 'unknown request error' fallback) caused each attempt to fail. Any caller of solveVerification or fetchBody will see this error when the upstream qiwei server is unreachable or keeps rejecting the request.
Solutions
- Inspect the wrapped %w cause in the error message to identify the underlying failure (timeout vs refused vs status)
- Verify network reachability: curl -v the qiwei URL from the host running the plugin
- If timeouts, increase the http.Client Timeout used by the plugin
- If repeated 4xx/5xx, refresh session cookies or wait for the upstream service to recover
Example fix
// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return err
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return fmt.Errorf("qiwei request timed out, check network: %w", err)
}
return err
} Defensive patterns
Strategy: retry
Validate before calling
// verify reachability before heavy flows
conn, err := net.DialTimeout("tcp", "open.weixin.qq.com:443", 3*time.Second)
if err != nil {
return fmt.Errorf("qiwei host unreachable: %w", err)
}
conn.Close() Try / catch
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return fmt.Errorf("qiwei upstream timeout after retries: %w", err)
}
return fmt.Errorf("qiwei request failed: %w", err)
} Prevention
- Ensure stable outbound network/DNS on the deployment host
- Configure proxy env vars (HTTP_PROXY/HTTPS_PROXY) if behind a corporate proxy
- Honor the plugin's built-in 3-retry backoff; add circuit-breaking on repeated failures
- Keep cookies/session state fresh to avoid repeated 4xx-driven retries
When it happens
Trigger: All 3 attempts of client.Do inside doRequestWithRetry fail — either with a transport error (DNS, timeout, TLS, connection refused) or a non-200 HTTP status; solveVerification/fetchBody then propagate this wrapped error.
Common situations: Corporate proxy/firewall blocking the host, DNS misconfiguration, TLS certificate problems (InsecureSkipVerify disabled upstream), the qiwei service being rate-limiting or down, or expired cookies causing repeated 4xx responses.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/35324b5a260587d4.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/qiwei/qiwei.go:661
for i := 0; i < maxRetries; i++ {
if i > 0 {
time.Sleep(time.Duration(1<<uint(i-1)) * 200 * time.Millisecond)
}
resp, err := client.Do(req.Clone(req.Context()))
if err == nil && resp != nil && resp.StatusCode == http.StatusOK {
return resp, nil
}
if resp != nil {
resp.Body.Close()
}
lastErr = err
}
if lastErr == nil {
lastErr = fmt.Errorf("unknown request error")
}
return nil, fmt.Errorf("[%s] 重试 %d 次后仍失败: %w", p.Name(), maxRetries, lastErr)
}
func (p *QiweiPlugin) setHeaders(req *http.Request, referer string) {
req.Header.Set("User-Agent", userAgent)
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,application/json;q=0.8,*/*;q=0.7")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Upgrade-Insecure-Requests", "1")
if referer != "" {
req.Header.Set("Referer", referer)
}
}
func (p *QiweiPlugin) hostCandidates() []string {
p.hostMu.RLock()
active := p.activeHost
p.hostMu.RUnlock()
View on GitHub (pinned to beaa561337)