{"record":{"id":"06f660cfcbc3eabd","repo":"fish2018/pansou","slug":"page-d-search-failed-w-06f660","errorCode":null,"errorMessage":"page %d search failed: %w","messagePattern":"page (.+?) search failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/yunso/yunso.go","lineNumber":108,"sourceCode":"// SearchWithResult 执行搜索并返回包含 IsFinal 标记的结果\nfunc (p *YunsoAsyncPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {\n\treturn p.AsyncSearchWithResult(keyword, p.doSearch, p.MainCacheKey, ext)\n}\n\n// doSearch 实际搜索实现\nfunc (p *YunsoAsyncPlugin) doSearch(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\tresultChan := make(chan []YunsoItem, yunsoDefaultMaxPages)\n\terrChan := make(chan error, yunsoDefaultMaxPages)\n\n\tvar wg sync.WaitGroup\n\tfor page := 1; page <= yunsoDefaultMaxPages; page++ {\n\t\twg.Add(1)\n\t\tgo func(pageNum int) {\n\t\t\tdefer wg.Done()\n\n\t\t\titems, err := p.searchPage(client, keyword, pageNum)\n\t\t\tif err != nil {\n\t\t\t\terrChan <- fmt.Errorf(\"page %d search failed: %w\", pageNum, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresultChan <- items\n\t\t}(page)\n\t}\n\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(resultChan)\n\t\tclose(errChan)\n\t}()\n\n\tvar allItems []YunsoItem\n\tfor items := range resultChan {\n\t\tallItems = append(allItems, items...)\n\t}\n\n\tvar errs []error","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/yunso/yunso.go#L90-L126","documentation":"yunso's Search fans out one goroutine per page; each goroutine calls searchPage and, on any failure, sends a wrapped error into errChan. This message means page N of the paginated search failed; the underlying cause (network, status, parse, API error) is carried by %w. Other pages may still have succeeded — results and errors are collected concurrently.","triggerScenarios":"Any searchPage failure for a given pageNum: HTTP request creation/transport error, non-200 status, body read/decode failure, apiResp.Code != 0, or HTML parse failure, all wrapped as \"page N search failed\".","commonSituations":"yunso.net is rate-limiting mid-search so later pages fail, the search API returns an error code (e.g. keyword blocked), or transient network drops cause some pages to fail while others succeed.","solutions":["Read the wrapped cause to see which stage failed (request, status, decode, api error, parse)","Retry only the failed pages instead of the whole search","Check whether the keyword triggers an API-side error (api returned error) and adjust the query","Add per-page retry with backoff before pushing into errChan","Verify network access to www.yunso.net and its API endpoints"],"exampleFix":"// before\nitems, err := p.searchPage(client, keyword, pageNum)\nif err != nil {\n    errChan <- fmt.Errorf(\"page %d search failed: %w\", pageNum, err)\n    return\n}\n// after\nvar items []YunsoItem\nvar err error\nfor attempt := 0; attempt < 3; attempt++ {\n    items, err = p.searchPage(client, keyword, pageNum)\n    if err == nil { break }\n    time.Sleep(time.Duration(attempt+1) * time.Second)\n}\nif err != nil {\n    errChan <- fmt.Errorf(\"page %d search failed: %w\", pageNum, err)\n    return\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"items, err := plugin.Search(ctx, keyword)\nif err != nil {\n    var pageErr *PageError\n    if errors.As(err, &pageErr) {\n        log.Warn(\"partial page failure\", \"page\", pageErr.Page, \"cause\", pageErr.Unwrap())\n    }\n    // fall back to partial results already received from resultChan\n}","preventionTips":["Collect partial results even when some pages fail","Add per-page retry with backoff inside the goroutine","Cap concurrency to avoid tripping the site's rate limits","Log wrapped causes with %v on errors.Unwrap"],"tags":["network","concurrency","pagination","scraping"],"backgroundTag":"http-request-failed","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}