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
- Unwrap and inspect the root error to distinguish timeout vs connection-refused vs TLS failure.
- Retry with a fresh context / longer timeout; the 30-second cap may be too short for slow detail pages.
- Verify the detail host is reachable (curl the exact detailURL) and adjust proxy settings if blocked.
- 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
- Treat individual detail-page failures as skippable, not fatal.
- Use adequate timeouts for slow detail pages.
- Send full browser headers to avoid connection-level bot blocking.
- Monitor which hosts fail persistently and cache the unreachability.
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)