{"record":{"id":"1d9e84b472879220","repo":"fish2018/pansou","slug":"s-w-1d9e84","errorCode":null,"errorMessage":"[%s] 执行搜索失败: %w","messagePattern":"\\[(.+?)\\] 执行搜索失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/diduan/diduan.go","lineNumber":139,"sourceCode":"\n// SearchWithResult 使用 BaseAsyncPlugin 的缓存和后台刷新能力。\nfunc (p *DiduanPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {\n\treturn p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)\n}\n\n// searchImpl 搜索实现\nfunc (p *DiduanPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\tif p.scraper == nil {\n\t\treturn nil, fmt.Errorf(\"[%s] Cloudflare 请求客户端未初始化\", p.Name())\n\t}\n\tif p.debugMode {\n\t\tlog.Printf(\"[DIDUAN] 开始搜索: %s\", keyword)\n\t}\n\n\t// 第一步：执行搜索获取结果列表\n\tsearchResults, err := p.executeSearch(keyword)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 执行搜索失败: %w\", p.Name(), err)\n\t}\n\n\tif p.debugMode {\n\t\tlog.Printf(\"[DIDUAN] 搜索获取到 %d 个结果\", len(searchResults))\n\t}\n\n\t// 第二步：并发获取详情页链接\n\tfinalResults := p.fetchDetailLinks(searchResults, keyword)\n\n\tif p.debugMode {\n\t\tlog.Printf(\"[DIDUAN] 最终获取到 %d 个有效结果\", len(finalResults))\n\t}\n\n\t// 第三步：关键词过滤（标准网盘插件需要过滤）\n\tfilteredResults := plugin.FilterResultsByKeyword(finalResults, keyword)\n\n\tif p.debugMode {\n\t\tlog.Printf(\"[DIDUAN] 关键词过滤后剩余 %d 个结果\", len(filteredResults))","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/diduan/diduan.go#L121-L157","documentation":"searchImpl wraps any failure from p.executeSearch(keyword) with this error, preserving the underlying cause via %w and prefixing the plugin name. It is the outer boundary error for the first phase of a Diduan search: fetching and extracting the result list.","triggerScenarios":"executeSearch returns an error — the page request failed (wrapped 搜索请求失败), a non-200 status or Cloudflare challenge occurred (httpStatusError), or goquery HTML parsing failed — and searchImpl propagates it wrapped.","commonSituations":"Cloudflare challenge pages returned to the scraper; site layout change causing parse or empty responses; network outages; the search endpoint URL changed so requests 404.","solutions":["Unwrap the error (errors.Unwrap or %w chain) to see whether it was a request failure, HTTP status, or parse failure.","If the message mentions a Cloudflare Managed Challenge, pause and retry later or improve scraper stealth/proxy setup.","Verify BaseURL/SearchPath still match the live site's endpoints.","Add retries/backoff for transient network errors."],"exampleFix":"// before\nresults, err := p.Search(keyword)\nif err != nil { return err }\n// after\nresults, err := p.Search(keyword)\nif err != nil {\n    log.Printf(\"diduan search failed: %v\", err) // %w chain shows root cause\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := p.Search(keyword)\nif err != nil {\n    log.Printf(\"search failed: %+v\", err) // preserve wrap chain\n    if strings.Contains(err.Error(), \"Managed Challenge\") {\n        // cool-down / proxy rotation path\n    }\n    return err\n}","preventionTips":["Log errors with %+v so the %w chain is preserved for diagnosis.","Classify wrapped causes (challenge vs status vs network) and handle each distinctly.","Keep BaseURL/SearchPath updated with the live site."],"tags":["search","error-wrapping","plugin","go"],"backgroundTag":"api-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"}