fish2018/pansou · error
详情页请求失败
Error message
详情页请求失败: %w
What it means
fetchMagnetLink sends the detail-page GET request through doRequestWithRetry, which retries client.Do up to MaxRetries with linear backoff. This error wraps the last transport-level failure after all retries are exhausted: DNS failure, connection refused/reset, TLS errors, or the per-request timeout context expiring.
Solutions
- Unwrap with errors.Unwrap / %w chain and check for context.DeadlineExceeded vs connection errors to distinguish timeout from unreachability.
- Increase TimeoutSeconds if the detail page is slow but reachable (test with curl from the same host).
- Verify network egress from the deployment environment (DNS resolution, proxy env vars HTTPS_PROXY).
- Check whether the site's domain changed and update the plugin's base URL configuration.
- Ensure the HTTP client has appropriate proxy/TLS settings if operating behind a firewall.
Example fix
// before
if _, err := p.fetchMagnetLink(client, detailURL); err != nil {
log.Fatal(err)
}
// after
if _, err := p.fetchMagnetLink(client, detailURL); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Printf("detail page timed out, skipping: %s", detailURL)
} else {
log.Printf("detail page unreachable, skipping: %s (%v)", detailURL, err)
}
} Defensive patterns
Strategy: retry
Validate before calling
// optional pre-check
resp, err := http.Head(detailURL)
if err != nil { /* host unreachable; skip enrichment for this URL */ } Try / catch
magnet, err := p.fetchMagnetLink(client, detailURL)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Printf("timeout fetching %s", detailURL)
}
return "" // degrade gracefully
} Prevention
- Right-size TimeoutSeconds for the slowest realistic page
- Keep MaxRetries with backoff enabled for flaky targets
- Verify egress/proxy config in deployment environments
- Monitor for site domain changes and update the base URL promptly
When it happens
Trigger: All MaxRetries attempts of client.Do on the detail-page request fail — site unreachable, TLS handshake failure, connection reset, or the TimeoutSeconds context deadline exceeded. The wrapped error is doRequestWithRetry's '请求失败,已重试%d次' error.
Common situations: The wuji detail site is down or blocked from the deployment region; no outbound internet in the container; corporate proxy/firewall dropping HTTPS; the site changed domains; the context timeout is too short for a slow site.
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/b36524d41523d068.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/wuji/wuji.go:317
}
}
// 创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), TimeoutSeconds*time.Second)
defer cancel()
// 创建请求
req, err := http.NewRequestWithContext(ctx, "GET", detailURL, nil)
if err != nil {
return "", fmt.Errorf("创建详情页请求失败: %w", err)
}
// 设置请求头
p.setRequestHeaders(req)
// 发送HTTP请求
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return "", fmt.Errorf("详情页请求失败: %w", err)
}
defer resp.Body.Close()
// 检查状态码
if resp.StatusCode != 200 {
return "", fmt.Errorf("详情页返回状态码: %d", resp.StatusCode)
}
// 读取响应体内容
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取详情页响应失败: %w", err)
}
// 解析HTML
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
return "", fmt.Errorf("详情页HTML解析失败: %w", err)View on GitHub (pinned to beaa561337)