{"record":{"id":"842fa23fedc82a2d","repo":"fish2018/pansou","slug":"s-w-842fa2","errorCode":null,"errorMessage":"[%s] 搜索请求失败: %w","messagePattern":"\\[(.+?)\\] 搜索请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/kkv/kkv.go","lineNumber":130,"sourceCode":"\t\n\treturn filtered\n}\n\nfunc (p *KKVPlugin) fetchSearchResults(searchURL string, client *http.Client) ([]searchItem, error) {\n\tdebugPrintf(\"🌐 请求搜索页面: %s\\n\", searchURL)\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\t\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\t\n\tp.setHeaders(req, baseURL)\n\t\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\t\n\tdebugPrintf(\"📡 HTTP状态码: %d\\n\", resp.StatusCode)\n\t\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\t\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] HTML解析失败: %w\", p.Name(), err)\n\t}\n\t\n\tvar items []searchItem\n\tdoc.Find(\"article.post\").Each(func(i int, s *goquery.Selection) {\n\t\tlink := s.Find(\".entry-header h2.entry-title a\")\n\t\thref, exists := link.Attr(\"href\")","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/kkv/kkv.go#L112-L148","documentation":"fetchSearchResults wraps the error returned by doRequestWithRetry after the request creation succeeded. doRequestWithRetry already retried the request maxRetries times, so this error means the kkv search page could not be fetched at all — network failure, timeout, TLS error, or the server refused/dropped the connection on every attempt.","triggerScenarios":"p.doRequestWithRetry(req, client) returns err after exhausting retries: DNS failure for the kkv domain, 30s context timeout, TLS handshake failure, or connection refused/reset each time.","commonSituations":"The kkv site is blocked or unreachable from the server's network (geo-block, GFW), DNS resolution fails for a dead mirror, the server has no outbound internet, or the 30-second timeout expires on a slow site.","solutions":["Verify the kkv base URL resolves and is reachable from the deployment machine (curl the search URL there)","Check the wrapped error: if it is a context deadline, consider raising the 30s timeout; if connection refused, the domain is wrong or dead","Use a proxy or mirror domain if the source is network-blocked at the deployment location","Confirm the app has outbound internet/DNS access (containers often lack it)"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // slow/blocked upstream","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", host+\":443\", 5*time.Second)\nif err != nil {\n    // upstream unreachable before even calling Search\n}","typeGuard":"func isTimeoutErr(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) && ne.Timeout() || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"results, err := plugin.Search(ctx, kw)\nif err != nil {\n    if isTimeoutErr(err) {\n        ctx2, cancel := context.WithTimeout(context.Background(), 90*time.Second)\n        defer cancel()\n        return plugin.Search(ctx2, kw)\n    }\n    log.Printf(\"search transport failed: %v\", err)\n    return nil\n}","preventionTips":["Check outbound internet/DNS from the deployment environment first","Pre-probe source availability with a cheap HEAD request before batch searches","Keep timeouts generous for slow overseas sources","Configure a proxy when sources are geo-blocked"],"tags":["network","http","retry","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"}