fish2018/pansou · error

[ ] 创建请求失败

Error message

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

What it means

The kkv plugin's fetchSearchResults wraps an error from http.NewRequestWithContext when building the search GET request. This means the request object itself could not be constructed — almost always because the search URL is unparseable (invalid scheme, control characters, bad host). It is a local client-side error, not a server problem.

Solutions

  1. Validate/URL-encode the search keyword (url.QueryEscape) before it is inserted into the search URL
  2. Check the configured kkv base URL has scheme http(s):// and a valid host; fix the plugin config or site list
  3. Print the failing searchURL alongside the wrapped error to see exactly what string NewRequest rejected
  4. Update the plugin's site base URL if the source moved domains

Example fix

// before
searchURL := fmt.Sprintf("%s/search/%s/page/1", baseURL, keyword)
// after
searchURL := fmt.Sprintf("%s/search/%s/page/1", strings.TrimRight(baseURL, "/"), url.QueryEscape(keyword))
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(searchURL)
if err != nil || u.Scheme == "" || u.Host == "" {
    // do not call the plugin with this URL/keyword
}

Type guard

func isValidURL(s string) bool {
    u, err := url.Parse(s)
    return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}

Try / catch

results, err := plugin.Search(ctx, kw)
if err != nil {
    var urlErr *url.Error
    if errors.As(err, &urlErr) {
        log.Printf("bad request URL %q: %v", urlErr.URL, urlErr.Err)
    }
    return nil
}

Prevention

When it happens

Trigger: http.NewRequestWithContext returns non-nil err while constructing the GET for searchURL, e.g. a malformed or empty base URL configured for the kkv source, an unescaped keyword containing spaces/control chars interpolated into the URL, or an invalid context.

Common situations: Misconfigured kkv base URL (typo like 'htp://' or missing scheme), keyword not URL-encoded before being put into the search URL format string, stale base-site URL after the site changed domains.

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/9595b49271cb3b1a. Report an issue: GitHub.

Appendix: source

Thrown at plugin/kkv/kkv.go:123

		if strings.Contains(lowerTitle, lowerKeyword) {
			debugPrintf("✅ 标题匹配: %s\n", item.Title)
			filtered = append(filtered, item)
		} else {
			debugPrintf("❌ 标题不匹配,跳过: %s\n", item.Title)
		}
	}
	
	return filtered
}

func (p *KKVPlugin) fetchSearchResults(searchURL string, client *http.Client) ([]searchItem, error) {
	debugPrintf("🌐 请求搜索页面: %s\n", searchURL)
	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.setHeaders(req, baseURL)
	
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()
	
	debugPrintf("📡 HTTP状态码: %d\n", resp.StatusCode)
	
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
	}
	
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {

View on GitHub (pinned to beaa561337)