fish2018/pansou · error
[ ] 创建请求失败
Error message
[%s] 创建请求失败: %w
What it means
CldiPlugin.searchPage wraps http.NewRequestWithContext failures with the plugin name. This error is rare in practice: it fires only when the method/URL are syntactically invalid or the context is nil, since a malformed URL cannot be parsed into a *http.Request.
Solutions
- Log/print searchURL and validate it with url.Parse before constructing the request.
- Ensure the base URL includes a scheme (https://) and is properly URL-escaped.
- Fix the plugin config value holding the endpoint URL.
Example fix
// before
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
// after
u, perr := url.Parse(searchURL)
if perr != nil {
return nil, fmt.Errorf("[%s] 无效搜索URL %q: %w", p.Name(), searchURL, perr)
}
req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
} Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(searchURL)
if err != nil || u.Scheme == "" || u.Host == "" {
return fmt.Errorf("无效搜索URL: %q", searchURL)
} Type guard
func isValidURL(raw string) bool {
u, err := url.Parse(raw)
return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
} Try / catch
results, err := plugin.Search(ctx, keyword)
if err != nil {
if strings.Contains(err.Error(), "创建请求失败") {
// config bug: log the URL and disable this plugin rather than retrying
}
} Prevention
- Validate configured base URLs at plugin init time with url.Parse
- URL-escape keyword parameters before building the search URL
- Always include the scheme in configured endpoints
- Fail fast at startup if a plugin's endpoint is malformed
When it happens
Trigger: http.NewRequestWithContext(ctx, "GET", searchURL, nil) returns err — searchURL is malformed (bad scheme, invalid characters, unparsable host) in searchPage.
Common situations: Configuration supplying a wrong or partially-updated base URL (e.g. missing scheme, spaces, or CJK characters not escaped), or a corrupted plugin config value.
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/caf9e924e5921656.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/cldi/cldi.go:136
}
// 3. 关键词过滤
return plugin.FilterResultsByKeyword(allResults, keyword), nil
}
// searchPage 搜索指定页面
func (p *CldiPlugin) searchPage(client *http.Client, keyword string, page int) ([]model.SearchResult, error) {
// 构建搜索URL (分类=0全部, 排序=2按添加时间)
searchURL := fmt.Sprintf("%s/search-%s-0-2-%d.html", baseURL, url.QueryEscape(keyword), page)
// 创建带超时的上下文
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.setRequestHeaders(req)
// 发送请求
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
// 检查状态码
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// 读取响应View on GitHub (pinned to beaa561337)