fish2018/pansou · error
[ ] 创建请求失败
Error message
[%s] 创建请求失败: %w
What it means
The kkv plugin's fetchSearchResults wraps an error from http.NewRequestWithContext when building the search GET request. This means the request object itself could not be constructed — almost always because the search URL is unparseable (invalid scheme, control characters, bad host). It is a local client-side error, not a server problem.
Solutions
- Validate/URL-encode the search keyword (url.QueryEscape) before it is inserted into the search URL
- Check the configured kkv base URL has scheme http(s):// and a valid host; fix the plugin config or site list
- Print the failing searchURL alongside the wrapped error to see exactly what string NewRequest rejected
- Update the plugin's site base URL if the source moved domains
Example fix
// before
searchURL := fmt.Sprintf("%s/search/%s/page/1", baseURL, keyword)
// after
searchURL := fmt.Sprintf("%s/search/%s/page/1", strings.TrimRight(baseURL, "/"), url.QueryEscape(keyword)) Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(searchURL)
if err != nil || u.Scheme == "" || u.Host == "" {
// do not call the plugin with this URL/keyword
} Type guard
func isValidURL(s string) bool {
u, err := url.Parse(s)
return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
} Try / catch
results, err := plugin.Search(ctx, kw)
if err != nil {
var urlErr *url.Error
if errors.As(err, &urlErr) {
log.Printf("bad request URL %q: %v", urlErr.URL, urlErr.Err)
}
return nil
} Prevention
- Always url.QueryEscape user-supplied keywords before interpolation
- Validate configured base URLs at startup (scheme + host present)
- Trim whitespace from any URL that was scraped or read from config
- Log the full failing URL, not just the error text
When it happens
Trigger: http.NewRequestWithContext returns non-nil err while constructing the GET for searchURL, e.g. a malformed or empty base URL configured for the kkv source, an unescaped keyword containing spaces/control chars interpolated into the URL, or an invalid context.
Common situations: Misconfigured kkv base URL (typo like 'htp://' or missing scheme), keyword not URL-encoded before being put into the search URL format string, stale base-site URL after the site changed domains.
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/9595b49271cb3b1a.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/kkv/kkv.go:123
if strings.Contains(lowerTitle, lowerKeyword) {
debugPrintf("✅ 标题匹配: %s\n", item.Title)
filtered = append(filtered, item)
} else {
debugPrintf("❌ 标题不匹配,跳过: %s\n", item.Title)
}
}
return filtered
}
func (p *KKVPlugin) fetchSearchResults(searchURL string, client *http.Client) ([]searchItem, error) {
debugPrintf("🌐 请求搜索页面: %s\n", searchURL)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
p.setHeaders(req, baseURL)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
debugPrintf("📡 HTTP状态码: %d\n", resp.StatusCode)
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {View on GitHub (pinned to beaa561337)