fish2018/pansou · error
[ ] 创建第 页请求失败
Error message
[%s] 创建第%d页请求失败: %w
What it means
fetchPage in the yunsou plugin failed while constructing the GET request for a search results page via http.NewRequestWithContext. The error is prefixed with the plugin name and page number. Since the URL is built from a template with a keyword-derived path, this usually means the URL is malformed (e.g. special characters in the keyword path) or the context is already canceled.
Solutions
- URL-escape pathKeyword with url.PathEscape before formatting into the template
- Log requestURL on failure to spot malformed construction
- Check ctx.Err() before building the request
- Validate searchURLTemplate is a valid absolute URL
Example fix
// before
path = fmt.Sprintf("%s-%d", pathKeyword, page)
requestURL := fmt.Sprintf(searchURLTemplate, path)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建第%d页请求失败: %w", p.Name(), page, err)
}
// after
path = fmt.Sprintf("%s-%d", url.PathEscape(pathKeyword), page)
requestURL := fmt.Sprintf(searchURLTemplate, path)
if _, perr := url.Parse(requestURL); perr != nil {
return nil, fmt.Errorf("[%s] invalid page %d url %q: %w", p.Name(), page, requestURL, perr)
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建第%d页请求失败: %w", p.Name(), page, err)
} Defensive patterns
Strategy: validation
Validate before calling
if strings.TrimSpace(pathKeyword) == "" {
return errors.New("keyword path required")
}
esccaped := url.PathEscape(pathKeyword)
requestURL := fmt.Sprintf(searchURLTemplate, esccaped)
if _, err := url.Parse(requestURL); err != nil {
return fmt.Errorf("invalid request url: %w", err)
} Type guard
func validURL(s string) bool {
u, err := url.Parse(s)
return err == nil && u.Scheme != "" && u.Host != ""
} Try / catch
items, err := fetchPage(ctx, client, keyword, page)
if err != nil && strings.Contains(err.Error(), "创建第") {
log.Error("yunsou page request construction failed", "page", page, "err", err)
return nil, err
} Prevention
- Always url.PathEscape keyword-derived path segments
- Log the fully built URL on construction failure
- Check ctx.Err() before building requests
- Validate URL templates at startup with a dry-run parse
When it happens
Trigger: http.NewRequestWithContext fails building the request to searchURLTemplate with pathKeyword (and -page suffix) — invalid URL characters from an unescaped keyword, or an invalid/canceled ctx.
Common situations: Keywords containing spaces, slashes, or CJK characters placed into a path without escaping; a canceled parent context; a mistyped searchURLTemplate constant.
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/69bbd9101c9f158c.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/yunsou/yunsou.go:121
}
results = append(results, p.parseSearchResults(pageDoc)...)
}
if len(results) > maxResults {
results = results[:maxResults]
}
return plugin.FilterResultsByKeyword(results, keyword), nil
}
func (p *YunsouAsyncPlugin) fetchPage(ctx context.Context, client *http.Client, keyword string, page int) (*goquery.Document, error) {
pathKeyword := url.PathEscape(keyword)
path := pathKeyword
if page > 1 {
path = fmt.Sprintf("%s-%d", pathKeyword, page)
}
requestURL := fmt.Sprintf(searchURLTemplate, path)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建第%d页请求失败: %w", p.Name(), page, err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Referer", "https://wpys.cc/")
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析第%d页失败: %w", p.Name(), page, err)
}
return doc, nil
}
func (p *YunsouAsyncPlugin) doRequestWithRetry(req *http.Request, client *http.Client) (*http.Response, error) {View on GitHub (pinned to beaa561337)