fish2018/pansou · error
[ ] 创建 API 请求失败
Error message
[%s] 创建 API 请求失败: %w
What it means
dyyj.executeSearchAPI failed to construct the Flarum API GET request (http.NewRequestWithContext) for BaseURL + /api/discussions. Like the HTML path, this means the composed apiURL failed URL parsing — usually a bad parameter value in the encoded query.
Solutions
- Print apiURL and run url.Parse on it to reproduce the parse error
- Build query values with url.Values and Encode() (the code already calls params.Encode() — check what was put into params)
- Validate keyword: strip control characters / newlines before adding to params
- Validate BaseURL at startup
Example fix
// before
params.Set("filter[q]", keyword)
// after
keyword = strings.TrimSpace(keyword)
if keyword == "" || strings.ContainsAny(keyword, "\x00\n\r") {
return nil, fmt.Errorf("[%s] 无效搜索关键词", p.Name())
}
params.Set("filter[q]", keyword) Defensive patterns
Strategy: validation
Validate before calling
if _, err := url.Parse(apiURL); err != nil {
return fmt.Errorf("invalid api url %q: %w", apiURL, err)
} Type guard
func validAPIURL(raw string) bool {
u, err := url.Parse(raw)
return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
} Try / catch
req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建 API 请求失败: %w", p.Name(), err)
} Prevention
- Compose query strings exclusively via url.Values.Encode()
- Sanitize keywords (trim, strip control chars) before adding to params
- Validate BaseURL at startup, fail fast on malformed config
- Unit-test URL construction with adversarial keywords
When it happens
Trigger: http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil) returned err: apiURL unparsable because a query parameter contains control characters or invalid percent-encoding before params.Encode().
Common situations: Keyword containing special characters was inserted into params without proper encoding; BaseURL constant mis-edited; env override of the API host malformed.
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/03b63ddeed49f021.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/dyyj/dyyj.go:414
ID string `json:"id"`
Attributes struct {
ContentHTML string `json:"contentHtml"`
CreatedAt string `json:"createdAt"`
} `json:"attributes"`
}
func (p *DyyjPlugin) executeSearchAPI(client *http.Client, keyword string) ([]model.SearchResult, error) {
params := url.Values{}
params.Set("filter[q]", keyword)
params.Set("include", "mostRelevantPost")
params.Set("page[limit]", fmt.Sprintf("%d", MaxResults))
apiURL := BaseURL + "/api/discussions?" + params.Encode()
ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建 API 请求失败: %w", p.Name(), err)
}
req.Header.Set("User-Agent", UserAgent)
req.Header.Set("Accept", "application/vnd.api+json, application/json")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Referer", BaseURL+"/")
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] API 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
var payload dyyjAPIResponse
if err := encodingjson.NewDecoder(resp.Body).Decode(&payload); err != nil {
return nil, fmt.Errorf("[%s] 解析 API 响应失败: %w", p.Name(), err)
}
posts := make(map[string]dyyjIncluded, len(payload.Included))View on GitHub (pinned to beaa561337)