fish2018/pansou · error
[ ] 创建请求失败
Error message
[%s] 创建请求失败: %w
What it means
searchImpl in the alupan plugin wraps errors from http.NewRequestWithContext when building the GET request to https://www.aliupan.com/?s=<keyword>. With a plain method, an absolute URL, and a nil body, NewRequestWithContext essentially only fails on malformed URL parsing, so this indicates the constructed search URL could not be parsed into a valid *http.Request.
Solutions
- Check the exact keyword at runtime — log or inspect it for control/invalid URL characters.
- Ensure url.QueryEscape is applied to the keyword in the Sprintf call.
- Verify the base URL constant is a valid absolute URL (https://www.aliupan.com/).
- Confirm the context passed to NewRequestWithContext is non-nil (it is created on the line above; refactors may have broken that).
Example fix
// before
searchURL := fmt.Sprintf("https://www.aliupan.com/?s=%s", keyword)
// after
searchURL := fmt.Sprintf("https://www.aliupan.com/?s=%s", url.QueryEscape(keyword)) Defensive patterns
Strategy: validation
Validate before calling
// Validate the URL before building the request
u, err := url.Parse(fmt.Sprintf("https://www.aliupan.com/?s=%s", url.QueryEscape(keyword)))
if err != nil {
return nil, fmt.Errorf("invalid search URL: %w", err)
}
if u.Scheme != "https" || u.Host == "" {
return nil, fmt.Errorf("unexpected search URL: %s", u)
} Try / catch
if req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil); err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
} Prevention
- Always url.QueryEscape user-supplied keywords before embedding them in URLs.
- Keep the base URL as a validated constant checked at startup.
- Never pass a nil context into NewRequestWithContext.
- Add a startup self-test that builds a sample request to catch URL regressions early.
When it happens
Trigger: http.NewRequestWithContext returns an error — practically only when the formatted URL fails url.Parse (invalid characters/structure) or the context is nil. Since the URL is a fixed template with url.QueryEscape(keyword), this is rare.
Common situations: A code change removes the url.QueryEscape call so the raw keyword produces an invalid URL (e.g. contains spaces or control characters); the base URL string is edited into a malformed literal; a nil context is passed after refactoring.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/4a30dec0d7681776.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/alupan/alupan.go:126
}
return &http.Client{
Transport: transport,
Timeout: searchTimeout,
}
}
func (p *AlupanPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {
if p.client != nil {
client = p.client
}
searchURL := fmt.Sprintf("https://www.aliupan.com/?s=%s", url.QueryEscape(keyword))
ctx, cancel := context.WithTimeout(context.Background(), searchTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
setCommonHeaders(req, "https://www.aliupan.com/")
resp, err := p.doRequestWithRetry(req, client, searchMaxRetries)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("[%s] 搜索返回状态码: %d", p.Name(), resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}View on GitHub (pinned to beaa561337)