fish2018/pansou · error

请求重试 次仍失败

Error message

请求重试 %d 次仍失败: %w

What it means

This error is returned by doRequestWithRetry in the yulinshufa plugin after all retry attempts have been exhausted. It wraps the last underlying error (either a non-2xx status code reported as "状态码: %d" or the final transport error) so callers see both the retry count and root cause. It means the upstream yulinshufa site could not be reached or did not return a successful response within maxRetries attempts.

Solutions

  1. Inspect the wrapped %w cause to identify whether it was a status code or a transport error
  2. Curl the target URL from the same host to check connectivity and whether the site is blocking you
  3. Increase retry count or add backoff delays between retries in doRequestWithRetry
  4. Add/refresh required headers or cookies (User-Agent, anti-bot tokens) used by the plugin
  5. If the site bans by IP, route through a proxy or wait out the rate limit

Example fix

// before
items, err := fetchSearchItems(keyword) // opaque retry-exhausted error
// after
if errors.Is(err, context.DeadlineExceeded) {
    log.Warn("yulinshufa timeout, backing off")
    time.Sleep(5 * time.Second)
}
items, err := fetchSearchItems(keyword)
Defensive patterns

Strategy: retry

Validate before calling

// preflight
resp, err := http.Head("https://target-site.example/search")
if err != nil || resp.StatusCode >= 400 {
    log.Warn("upstream unreachable, skipping yulinshufa search")
}

Type guard

func isRetryExhausted(err error) bool {
    return err != nil && strings.Contains(err.Error(), "仍失败")
}

Try / catch

items, err := fetchSearchItems(ctx, keyword)
if err != nil {
    var retried *RetryExhaustedError
    if errors.As(err, &retried) {
        log.Warn("yulinshufa failed after retries", "cause", errors.Unwrap(err))
        return fallbackSearch(ctx, keyword)
    }
    return err
}

Prevention

When it happens

Trigger: fetchSearchItems or getDetailResult calls doRequestWithRetry and every attempt fails — e.g. the target site returns 403/429/5xx on all retries, DNS failure, TLS errors, or network timeouts.

Common situations: The site is down or blocking the scraper (IP rate-limit/ban), a required cookie/anti-bot token is missing, the container has no internet or broken DNS, or the site changed its URL/anti-scraping behavior.

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

Appendix: source

Thrown at plugin/yulinshufa/yulinshufa.go:753

		if attempt > 0 {
			backoff := time.Duration(1<<uint(attempt-1)) * 200 * time.Millisecond
			time.Sleep(backoff)
			p.debugf("重试第 %d 次, url=%s", attempt+1, req.URL.String())
		}

		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 = fmt.Errorf("状态码: %d", resp.StatusCode)
		} else {
			lastErr = err
		}
	}
	return nil, fmt.Errorf("请求重试 %d 次仍失败: %w", maxRetries, lastErr)
}

func parseDebugFlag() bool {
	value := strings.ToLower(strings.TrimSpace(os.Getenv("YULINSHUFA_DEBUG")))
	if value == "" {
		return false
	}
	return value == "1" || value == "true" || value == "yes" || value == "on"
}

func (p *YulinshufaPlugin) debugf(format string, args ...interface{}) {
	if p.debugMode {
		log.Printf("[YULINSHUFA] "+format, args...)
	}
}

View on GitHub (pinned to beaa561337)