fish2018/pansou · error
[ ] 创建请求失败
Error message
[%s] 创建请求失败: %w
What it means
dy4k's searchPage wraps a failure from http.NewRequestWithContext when constructing the GET request for one search results page. As with the duoduo plugin, this fires only when the page URL is unparseable or the context is invalid — before any network traffic. The plugin name is included for multi-source disambiguation.
Solutions
- Log searchURL before creating the request and inspect it for unescaped characters.
- Escape the keyword with url.QueryEscape when building the URL.
- Validate the composed URL with url.Parse before http.NewRequestWithContext.
- Confirm the configured base URL for dy4k is correct and reachable.
Example fix
// before
searchURL := fmt.Sprintf("%s/search/%s/%d", base, keyword, page)
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
// after
searchURL := fmt.Sprintf("%s/search/%s/%d", base, url.QueryEscape(keyword), page)
if _, perr := url.Parse(searchURL); perr != nil {
return nil, 0, fmt.Errorf("[%s] 无效搜索URL: %w", p.Name(), perr)
}
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil) Defensive patterns
Strategy: validation
Validate before calling
func buildSearchURL(base, keyword string, page int) (string, error) {
u := fmt.Sprintf("%s/search/%s/%d", base, url.QueryEscape(keyword), page)
parsed, err := url.Parse(u)
if err != nil { return "", err }
if parsed.Scheme == "" || parsed.Host == "" { return "", fmt.Errorf("invalid url: %s", u) }
return u, nil
} Type guard
func validRequestURL(s string) bool {
u, err := url.Parse(s)
return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
} Prevention
- Escape every dynamic URL segment.
- Compose URLs via url.Values / url.URL rather than Sprintf on raw strings.
- Validate base URL configuration at startup.
- Unit-test URL construction with CJK and special-character keywords.
When it happens
Trigger: The per-page searchURL built from the base URL, keyword, and page number is malformed (unescaped CJK keyword characters, spaces, or empty base URL), making http.NewRequestWithContext return an error.
Common situations: Keyword contains characters that were not url.QueryEscape'd; configuration changed the base URL to something invalid; page number formatting produced a URL with an unexpected token; empty keyword yields a path-only URL that trips parsing in edge cases.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/15d83b5d59d478e9.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/dy4k/dy4k.go:357
// 1. 构建搜索URL
var searchURL string
if page == 1 {
searchURL = fmt.Sprintf(SearchURL, encodedKeyword)
} else {
searchURL = fmt.Sprintf(SearchPageURL, encodedKeyword, page)
}
debugPrintf("🔧 [Dy4k DEBUG] 构建的URL: %s\n", searchURL)
// 2. 创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
// 3. 创建请求
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
if err != nil {
return nil, 0, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
// 4. 设置完整的请求头(包含随机UA和IP)
randomUA := getRandomUA()
randomIP := generateRandomIP()
req.Header.Set("User-Agent", randomUA)
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Upgrade-Insecure-Requests", "1")
req.Header.Set("Cache-Control", "max-age=0")
req.Header.Set("Referer", BaseURL+"/")
req.Header.Set("X-Forwarded-For", randomIP)
req.Header.Set("X-Real-IP", randomIP)
req.Header.Set("sec-ch-ua-platform", "macOS")
debugPrintf("🔧 [Dy4k DEBUG] 使用随机UA: %s\n", randomUA)View on GitHub (pinned to beaa561337)