fish2018/pansou · error

[ ] 创建第 页请求失败

Error message

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

What it means

fetchPage builds the per-page search GET request with http.NewRequestWithContext. If request construction fails (almost always an invalid URL or unencodable parameters), the error is wrapped with the plugin name and page number. This fires before any network I/O occurs.

Solutions

  1. Print requestURL on failure and inspect for missing scheme or illegal characters.
  2. Sanitize/validate the keyword before interpolation (strip control characters, re-run url.PathEscape).
  3. Check the searchURL format string still matches the current site URL scheme.
  4. Since NewRequestWithContext rarely fails for valid URLs, log the raw keyword that triggered it.

Example fix

// before
requestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))
// after
requestURL := fmt.Sprintf(searchURL, page, url.PathEscape(strings.TrimSpace(keyword)))
if _, err := url.Parse(requestURL); err != nil {
	return pageResult{}, fmt.Errorf("invalid search url %q: %w", requestURL, err)
}
Defensive patterns

Strategy: validation

Validate before calling

func validKeyword(k string) bool {
	for _, r := range k {
		if r < 0x20 || r == 0x7f { return false }
	}
	return strings.TrimSpace(k) != ""
}

Type guard

func isPrintableKeyword(s string) bool { return utf8.ValidString(s) && strings.TrimSpaceFunc(s, func(r rune) bool { return !unicode.IsPrint(r) }) == "" }

Try / catch

req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
	return pageResult{}, fmt.Errorf("[%s] page %d bad url %q: %w", p.Name(), page, requestURL, err)
}

Prevention

When it happens

Trigger: http.NewRequestWithContext returns an error for the URL built by fmt.Sprintf(searchURL, page, url.PathEscape(keyword)) — typically because keyword contains characters that produce a malformed URL, or searchURL template is misconfigured.

Common situations: Keyword containing control characters or an invalid escape sequence; searchURL constant edited incorrectly (bad %s placeholders or missing scheme); page number formatting producing an invalid path segment.

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/5fd2351588978a04. Report an issue: GitHub.

Appendix: source

Thrown at plugin/xiaoyu/xiaoyu.go:152

			if _, exists := seen[key]; exists {
				continue
			}
			seen[key] = struct{}{}
			results = append(results, result)
		}
	}

	return plugin.FilterResultsByKeyword(results, keyword), nil
}

func (p *XiaoyuPlugin) fetchPage(client *http.Client, keyword string, page int) (pageResult, error) {
	requestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))
	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
	if err != nil {
		return pageResult{}, fmt.Errorf("[%s] 创建第%d页请求失败: %w", p.Name(), page, err)
	}
	setRequestHeaders(req)

	resp, err := doRequestWithRetry(client, req)
	if err != nil {
		return pageResult{}, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
	}
	defer resp.Body.Close()

	doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))
	if err != nil {
		return pageResult{}, fmt.Errorf("[%s] 第%d页解析失败: %w", p.Name(), page, err)
	}

	return parsePage(doc), nil
}

func setRequestHeaders(req *http.Request) {

View on GitHub (pinned to beaa561337)