fish2018/pansou · error

[ ] 创建搜索请求失败

Error message

[%s] 创建搜索请求失败: %w

What it means

dyyj.executeSearchHTML could not construct the outbound *http.Request via http.NewRequestWithContext. This almost always means the searchURL failed URL parsing (url.Parse error). Rare, since searchURL is built from constants, but thrown defensively.

Solutions

  1. Print searchURL before the call and run url.Parse on it to see the exact parse error
  2. Ensure the keyword is escaped with url.QueryEscape / url.Values.Encode() before interpolation
  3. Fix the BaseURL/search-path constant — remove spaces, control chars, or bad %-sequences
  4. Validate configured site URLs at plugin startup

Example fix

// before
searchURL := BaseURL + "/search?q=" + keyword
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
// after
searchURL := BaseURL + "/search?" + url.Values{"q": {keyword}}.Encode()
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := url.Parse(searchURL); err != nil {
	return fmt.Errorf("invalid search URL %q: %w", searchURL, err)
}

Type guard

func validURL(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, "GET", searchURL, nil)
if err != nil {
	return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
}

Prevention

When it happens

Trigger: http.NewRequestWithContext(ctx, "GET", searchURL, nil) returned err — i.e. searchURL contains control characters, invalid percent-encodings, or is empty/unparsable.

Common situations: A configuration or constant BaseURL/search path was edited to include spaces or unescaped characters; keyword interpolation broke URL escaping; env-specific override of the site URL is 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/736d1ee75d1e59a0. Report an issue: GitHub.

Appendix: source

Thrown at plugin/dyyj/dyyj.go:241

// use if the forum API changes or becomes unavailable.
func (p *DyyjPlugin) executeSearchHTML(client *http.Client, keyword string) ([]model.SearchResult, error) {
	// 构建搜索URL
	searchURL := fmt.Sprintf("%s%s", BaseURL, fmt.Sprintf(SearchPath, url.QueryEscape(keyword)))

	if p.debugMode {
		log.Printf("[DYYJ] 搜索URL: %s", searchURL)
	}

	// 创建带超时的上下文
	ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
	if err != nil {
		if p.debugMode {
			log.Printf("[DYYJ] 创建搜索请求失败: %v", err)
		}
		return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
	}

	// 设置完整的请求头
	req.Header.Set("User-Agent", UserAgent)
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("Cache-Control", "max-age=0")
	req.Header.Set("Referer", BaseURL+"/")

	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		if p.debugMode {
			log.Printf("[DYYJ] 搜索请求失败: %v", err)
		}
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}

View on GitHub (pinned to beaa561337)