{"record":{"id":"1d13082b63a64dfd","repo":"fish2018/pansou","slug":"w-1d1308","errorCode":null,"errorMessage":"请求失败: %w","messagePattern":"请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/susu/susu.go","lineNumber":161,"sourceCode":"\tsearchURL := fmt.Sprintf(SearchURL, url.QueryEscape(keyword))\n\n\t// 发送请求\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\t// 设置请求头\n\treq.Header.Set(\"User-Agent\", getRandomUA())\n\tsetBrowserHeaders(req, BaseURL+\"/\")\n\n\t// 发送请求（带重试）\n\tresp, err := p.doRequestWithRetry(client, req, MaxRetries)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[susu] 搜索请求返回状态码: %d\", resp.StatusCode)\n\t}\n\n\t// 解析HTML\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"解析HTML失败: %w\", err)\n\t}\n\n\t// 提取搜索结果\n\tvar wg sync.WaitGroup\n\tresultChan := make(chan model.SearchResult, 20)\n\n\t// 创建信号量控制并发数\n\tsemaphore := make(chan struct{}, MaxConcurrency)","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/susu/susu.go#L143-L179","documentation":"doSearch in the susu plugin wraps any error from doRequestWithRetry (the network layer performing the HTTP search request to BaseURL) with the message \"请求失败\" (\"request failed\"), preserving the underlying cause via %w. It is thrown whenever the retried HTTP search request ultimately fails — DNS failure, connection refused, TLS errors, timeouts, or retries exhausted. It signals the search never produced an HTTP response at all.","triggerScenarios":"Calling doSearch (directly or via the plugin's public Search, e.g. from TestSusuLiveSearch) when doRequestWithRetry exhausts MaxRetries without receiving a response: network unreachable, host resolution failure, TLS handshake failure, or client timeout on the search endpoint.","commonSituations":"Offline or firewalled environments; the susu site blocking the server's IP; DNS failures in containers; proxy misconfiguration; the target site being down or moving domains; MaxRetries being too low for a flaky network.","solutions":["Check network connectivity to the susu site (curl the BaseURL from the same host) and confirm DNS resolves.","Inspect the wrapped error (%w chain) with errors.Unwrap / %v to see the root cause (timeout vs connection refused vs TLS).","Increase the HTTP client timeout and MaxRetries if the site is slow or intermittently failing.","If the site blocks datacenter IPs, route through a working proxy or update BaseURL if the domain changed.","For tests like TestSusuLiveSearch, ensure the test environment has internet access or run against a local mock server."],"exampleFix":"// before\nresp, err := p.doRequestWithRetry(client, req, MaxRetries)\nif err != nil {\n    return nil, fmt.Errorf(\"请求失败: %w\", err)\n}\n// after: unwrap and log root cause for easier debugging\nresp, err := p.doRequestWithRetry(client, req, MaxRetries)\nif err != nil {\n    return nil, fmt.Errorf(\"请求失败: %w\", err) // use %v in logs to print the full chain\n}","handlingStrategy":"retry","validationCode":"// check reachability before calling Search\nconn, err := net.DialTimeout(\"tcp\", \"susu-host:443\", 5*time.Second)\nif err != nil { /* skip search, host unreachable */ }\nconn.Close()","typeGuard":null,"tryCatchPattern":"links, err := p.Search(query)\nif err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        // back off and retry later\n    }\n    log.Printf(\"susu search unavailable: %v\", err)\n    return fallbackResults\n}","preventionTips":["Verify outbound network/DNS in the deployment environment","Configure sane client timeout and MaxRetries","Monitor the upstream site's availability","Provide a fallback search source when this plugin fails"],"tags":["network","http-client","search","retry-exhausted"],"backgroundTag":"network-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"}