fish2018/pansou · error

[ ] 搜索第一页失败

Error message

[%s] 搜索第一页失败: %w

What it means

WujiPlugin.searchImpl wraps any error from fetching/parsing page 1 of search results as "[%s] 搜索第一页失败" ("first page search failed"), including the plugin name and the underlying cause via %w. Since page 1 anchors pagination of all subsequent pages, the whole search is aborted when it fails. The root cause is always in the wrapped error (network, HTTP status, or parse failure inside searchPage).

Solutions

  1. Unwrap and read the inner %w error to identify network vs HTTP-status vs parse failure.
  2. Retry the search — transient upstream failures and rate limits often clear; add backoff.
  3. Verify the Wuji search endpoint is reachable (curl the same URL with the same keyword) to rule out site downtime.
  4. If parsing fails, check whether Wuji changed its response format and update searchPage.
  5. Confirm the http.Client has sane timeout/proxy/TLS settings for the environment.

Example fix

// before
firstPageResults, err := p.searchPage(client, keyword, 1)
if err != nil {
    return nil, fmt.Errorf("[%s] 搜索第一页失败: %w", p.Name(), err)
}
// after: one retry with backoff before failing
firstPageResults, err := p.searchPage(client, keyword, 1)
if err != nil {
    time.Sleep(time.Second)
    firstPageResults, err = p.searchPage(client, keyword, 1)
    if err != nil {
        return nil, fmt.Errorf("[%s] 搜索第一页失败: %w", p.Name(), err)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// Go: cheap reachability pre-check before running a multi-page search
resp, err := client.Head(searchBaseURL)
if err != nil || resp.StatusCode >= 500 {
    return fmt.Errorf("search upstream unreachable (status=%v, err=%v)", statusCodeOr(resp), err)
}

Try / catch

results, err := plugin.Search(keyword, ext)
if err != nil {
    var wrapped interface{ Unwrap() error }
    if errors.As(err, &wrapped) || strings.Contains(err.Error(), "搜索第一页失败") {
        // transient upstream failure: backoff and retry once
        time.Sleep(2 * time.Second)
        results, err = plugin.Search(keyword, ext)
    }
    if err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: searchImpl calls p.searchPage(client, keyword, 1) and it returns an error — e.g. the HTTP request to the Wuji search endpoint fails, times out, returns a non-200 status, or the response cannot be parsed into results.

Common situations: Wuji site is down or rate-limiting the client; keyword triggers a server-side error; network/DNS/proxy problems in the environment; the site changed its response layout so searchPage's parsing fails; TLS or timeout misconfiguration in the passed http.Client.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/dfa819ffbabb2996. Report an issue: GitHub.

Appendix: source

Thrown at plugin/wuji/wuji.go:105

func (p *WujiPlugin) Search(keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {
	result, err := p.SearchWithResult(keyword, ext)
	if err != nil {
		return nil, err
	}
	return result.Results, nil
}

// SearchWithResult 执行搜索并返回包含IsFinal标记的结果
func (p *WujiPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {
	return p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)
}

// searchImpl 实际的搜索实现
func (p *WujiPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {
	// 1. 首先搜索第一页
	firstPageResults, err := p.searchPage(client, keyword, 1)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索第一页失败: %w", p.Name(), err)
	}
	
	// 存储所有结果
	var allResults []model.SearchResult
	allResults = append(allResults, firstPageResults...)
	
	// 2. 并发搜索其他页面(第2页到第5页)
	if MaxPages > 1 {
		var wg sync.WaitGroup
		var mu sync.Mutex
		
		// 使用信号量控制并发数
		semaphore := make(chan struct{}, MaxConcurrency)
		
		// 存储每页结果
		pageResults := make(map[int][]model.SearchResult)
		
		for page := 2; page <= MaxPages; page++ {

View on GitHub (pinned to beaa561337)