fish2018/pansou · error

详情页请求失败

Error message

详情页请求失败: %w

What it means

fetchDetailPageLinks wraps any error returned by doRequestWithRetry for the detail-page request. All retry attempts for fetching the detail page failed at the transport level (connection, timeout, TLS), and the wrapped retry-exhaustion error is included via %w.

Solutions

  1. Unwrap and inspect the root error to distinguish timeout vs connection-refused vs TLS failure.
  2. Retry with a fresh context / longer timeout; the 30-second cap may be too short for slow detail pages.
  3. Verify the detail host is reachable (curl the exact detailURL) and adjust proxy settings if blocked.
  4. Consider treating per-link failures as non-fatal: skip the failing detail page instead of aborting the whole search.

Example fix

// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    return nil, fmt.Errorf("详情页请求失败: %w", err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    log.Printf("详情页 %s 请求失败,跳过: %v", detailURL, err)
    return nil, nil // skip this detail page, continue search
}
Defensive patterns

Strategy: fallback

Validate before calling

conn, err := net.DialTimeout("tcp", host+":443", 5*time.Second)
if err != nil {
    log.Println("detail host unreachable, skip")
    return
}
conn.Close()

Type guard

func isTransportError(err error) bool {
    var urlErr *url.Error
    return errors.As(err, &urlErr)
}

Try / catch

links, err := plugin.Search(keyword)
if err != nil {
    if strings.Contains(err.Error(), "详情页请求失败") {
        // per-link failure: degrade instead of aborting
        return partialResults, nil
    }
    return nil, err
}

Prevention

When it happens

Trigger: p.doRequestWithRetry(req, client) returns err after MaxRetries failed attempts on the detail URL — site unreachable, request context (30s) deadline exceeded, or connection reset on every try.

Common situations: Anti-bot protection (403/connection drop on detail pages but search works); detail pages on a different CDN host that is blocked; network outage mid-search while iterating detail links.

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

Appendix: source

Thrown at plugin/pianku/pianku.go:393

// fetchDetailPageLinks 获取详情页的下载链接
func (p *PiankuPlugin) fetchDetailPageLinks(client *http.Client, detailURL string) ([]model.Link, error) {
	// 创建带超时的上下文
	ctx, cancel := context.WithTimeout(context.Background(), TimeoutSeconds*time.Second)
	defer cancel()
	
	// 创建请求
	req, err := http.NewRequestWithContext(ctx, "GET", detailURL, nil)
	if err != nil {
		return nil, fmt.Errorf("创建详情页请求失败: %w", err)
	}
	
	// 设置请求头
	p.setRequestHeaders(req)
	
	// 发送HTTP请求
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("详情页请求失败: %w", err)
	}
	defer resp.Body.Close()
	
	// 检查状态码
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("详情页请求返回状态码: %d", resp.StatusCode)
	}
	
	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("详情页HTML解析失败: %w", err)
	}
	
	// 提取下载链接
	return p.extractDownloadLinks(doc), nil
}

View on GitHub (pinned to beaa561337)