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

  1. Print apiURL and run url.Parse on it to reproduce the parse error
  2. Build query values with url.Values and Encode() (the code already calls params.Encode() — check what was put into params)
  3. Validate keyword: strip control characters / newlines before adding to params
  4. 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

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)