{"record":{"id":"bbbaed2cdc93db8d","repo":"fish2018/pansou","slug":"s-w-bbbaed","errorCode":null,"errorMessage":"[%s] 执行搜索失败: %w","messagePattern":"\\[(.+?)\\] 执行搜索失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/hdmoli/hdmoli.go","lineNumber":86,"sourceCode":"func (p *HdmoliPlugin) Description() string {\n\treturn Description\n}\n\n// Search 搜索接口\nfunc (p *HdmoliPlugin) Search(keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\treturn p.searchImpl(&http.Client{Timeout: 30 * time.Second}, keyword, ext)\n}\n\n// searchImpl 搜索实现\nfunc (p *HdmoliPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\tif p.debugMode {\n\t\tlog.Printf(\"[HDMOLI] 开始搜索: %s\", keyword)\n\t}\n\n\t// 第一步：执行搜索获取结果列表\n\tsearchResults, err := p.executeSearch(client, 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(\"[HDMOLI] 搜索获取到 %d 个结果\", len(searchResults))\n\t}\n\n\t// 第二步：并发获取详情页链接\n\tfinalResults := p.fetchDetailLinks(client, searchResults, keyword)\n\n\tif p.debugMode {\n\t\tlog.Printf(\"[HDMOLI] 最终获取到 %d 个有效结果\", len(finalResults))\n\t}\n\n\t// 第三步：关键词过滤（标准网盘插件需要过滤）\n\tfilteredResults := plugin.FilterResultsByKeyword(finalResults, keyword)\n\n\tif p.debugMode {\n\t\tlog.Printf(\"[HDMOLI] 关键词过滤后剩余 %d 个结果\", len(filteredResults))","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/hdmoli/hdmoli.go#L68-L104","documentation":"hdmoli's searchImpl wraps any failure from executeSearch (request creation, HTTP failure, non-200 status, HTML parse error) with 执行搜索失败. It is a top-level aggregation error meaning the HDMOLI search pipeline failed before results could be returned; the wrapped error carries the root cause.","triggerScenarios":"Any call to the plugin's Search that results in executeSearch returning an error: network failure, DNS failure, timeout, non-200 response, or unparseable HTML from hdmoli's search endpoint.","commonSituations":"The hdmoli site is down or blocked (GFW/ISP), the search URL structure changed, rate limiting returns non-200 pages, or the machine has no internet access.","solutions":["Inspect the wrapped %w cause to identify the actual failure layer","Check network connectivity and whether hdmoli is reachable (curl the BaseURL)","Update the search URL/selectors if the site's structure changed","Rely on doRequestWithRetry: increase maxRetries or backoff for flaky upstream"],"exampleFix":"// before\nresults, err := plugin.Search(keyword)\nif err != nil {\n    return err\n}\n// after\nresults, err := plugin.Search(keyword)\nif err != nil {\n    log.Printf(\"hdmoli search failed: %v\", err) // read wrapped cause\n    return nil // degrade gracefully, let other plugins answer\n}","handlingStrategy":"retry","validationCode":"// Go: pre-check upstream reachability before searching\nresp, err := http.Head(BaseURL)\nif err != nil || resp.StatusCode >= 500 {\n    return fmt.Errorf(\"hdmoli unreachable, skipping search\")\n}","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword)\nif err != nil {\n    log.Printf(\"hdmoli search failed: %v\", err) // inspect wrapped cause\n    results = nil // degrade gracefully, other plugins may still answer\n}","preventionTips":["Log the fully unwrapped error chain (errors.Unwrap / %v of chain)","Monitor upstream site availability and URL structure","Keep retry/backoff tuned for flaky scrapers","Aggregate multi-plugin searches so one failing plugin doesn't kill the request"],"tags":["go","network","web-scraping","error-wrapping"],"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"}