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

  1. Unwrap with errors.Unwrap / %w chain and check for context.DeadlineExceeded vs connection errors to distinguish timeout from unreachability.
  2. Increase TimeoutSeconds if the detail page is slow but reachable (test with curl from the same host).
  3. Verify network egress from the deployment environment (DNS resolution, proxy env vars HTTPS_PROXY).
  4. Check whether the site's domain changed and update the plugin's base URL configuration.
  5. 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

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)