fish2018/pansou · error

[ ] 搜索第一页失败

Error message

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

What it means

CldiPlugin.searchImpl wraps a failure from searchPage on page 1 with the plugin name for context. The wrapped error comes from request creation, request execution, status-code check, body read, or HTML parsing. It signals the initial search page could not be retrieved, so no results are returned.

Solutions

  1. Inspect the wrapped cause (%w) to determine whether it was request creation, transport, status code, body read, or parsing.
  2. Test the CLDI search URL manually with curl using the same headers.
  3. Check for rate limiting or IP blocking; add backoff or proxy rotation.
  4. Verify the endpoint URL and page-1 parameters are still valid.
Defensive patterns

Strategy: try-catch

Try / catch

results, err := plugin.Search(ctx, keyword)
if err != nil {
    if strings.Contains(err.Error(), "搜索第一页失败") {
        log.Printf("CLDI first page failed, skipping plugin: %v", err)
        // continue with other plugins' results
    }
}

Prevention

When it happens

Trigger: p.searchPage(client, keyword, 1) returns an error in searchImpl — e.g. the first page request timed out, was rate-limited, or its HTML couldn't be parsed.

Common situations: The CLDI site is down or blocking the client, network outage, or the search endpoint changed causing repeated failures.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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

Appendix: source

Thrown at plugin/cldi/cldi.go:71

func (p *CldiPlugin) 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 *CldiPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {
	return p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)
}

// searchImpl 实际的搜索实现
func (p *CldiPlugin) 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)