fish2018/pansou · error
[ ] 创建第 页请求失败
Error message
[%s] 创建第%d页请求失败: %w
What it means
fetchPage builds the per-page search GET request with http.NewRequestWithContext. If request construction fails (almost always an invalid URL or unencodable parameters), the error is wrapped with the plugin name and page number. This fires before any network I/O occurs.
Solutions
- Print requestURL on failure and inspect for missing scheme or illegal characters.
- Sanitize/validate the keyword before interpolation (strip control characters, re-run url.PathEscape).
- Check the searchURL format string still matches the current site URL scheme.
- Since NewRequestWithContext rarely fails for valid URLs, log the raw keyword that triggered it.
Example fix
// before
requestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))
// after
requestURL := fmt.Sprintf(searchURL, page, url.PathEscape(strings.TrimSpace(keyword)))
if _, err := url.Parse(requestURL); err != nil {
return pageResult{}, fmt.Errorf("invalid search url %q: %w", requestURL, err)
} Defensive patterns
Strategy: validation
Validate before calling
func validKeyword(k string) bool {
for _, r := range k {
if r < 0x20 || r == 0x7f { return false }
}
return strings.TrimSpace(k) != ""
} Type guard
func isPrintableKeyword(s string) bool { return utf8.ValidString(s) && strings.TrimSpaceFunc(s, func(r rune) bool { return !unicode.IsPrint(r) }) == "" } Try / catch
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return pageResult{}, fmt.Errorf("[%s] page %d bad url %q: %w", p.Name(), page, requestURL, err)
} Prevention
- Validate/sanitize keywords (trim, strip control chars) before building the URL.
- Sanity-check the assembled URL with url.Parse before constructing the request.
- Keep the searchURL format string under test so edits cannot silently break it.
When it happens
Trigger: http.NewRequestWithContext returns an error for the URL built by fmt.Sprintf(searchURL, page, url.PathEscape(keyword)) — typically because keyword contains characters that produce a malformed URL, or searchURL template is misconfigured.
Common situations: Keyword containing control characters or an invalid escape sequence; searchURL constant edited incorrectly (bad %s placeholders or missing scheme); page number formatting producing an invalid path segment.
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/5fd2351588978a04.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/xiaoyu/xiaoyu.go:152
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
results = append(results, result)
}
}
return plugin.FilterResultsByKeyword(results, keyword), nil
}
func (p *XiaoyuPlugin) fetchPage(client *http.Client, keyword string, page int) (pageResult, error) {
requestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return pageResult{}, fmt.Errorf("[%s] 创建第%d页请求失败: %w", p.Name(), page, err)
}
setRequestHeaders(req)
resp, err := doRequestWithRetry(client, req)
if err != nil {
return pageResult{}, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))
if err != nil {
return pageResult{}, fmt.Errorf("[%s] 第%d页解析失败: %w", p.Name(), page, err)
}
return parsePage(doc), nil
}
func setRequestHeaders(req *http.Request) {View on GitHub (pinned to beaa561337)