{"record":{"id":"1a414bcfbf3725d3","repo":"fish2018/pansou","slug":"s-w-1a414b","errorCode":null,"errorMessage":"[%s] 搜索请求失败: %w","messagePattern":"\\[(.+?)\\] 搜索请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/wuji/wuji.go","lineNumber":191,"sourceCode":"\tsearchURL := fmt.Sprintf(SearchURL, encodedKeyword, page)\n\t\n\t// 创建带超时的上下文\n\tctx, cancel := context.WithTimeout(context.Background(), TimeoutSeconds*time.Second)\n\tdefer cancel()\n\t\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\t// 设置请求头\n\tp.setRequestHeaders(req)\n\t\n\t// 发送HTTP请求\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\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\t\n\t// 读取响应体内容\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取响应失败: %w\", p.Name(), err)\n\t}\n\t\n\t// 解析HTML\n\tdoc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] HTML解析失败: %w\", p.Name(), err)","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/wuji/wuji.go#L173-L209","documentation":"The wuji plugin's searchPage wraps the error from doRequestWithRetry, which performs the HTTP GET (with up to MaxRetries=3 attempts). Any transport-level failure — DNS resolution, TCP connect, TLS handshake, or the 30s context timeout — surfaces here wrapped with the plugin name. This is a client-side/network error, not an HTTP status problem (status codes are handled separately).","triggerScenarios":"p.doRequestWithRetry(req, client) returns an error after exhausting retries: site unreachable, DNS failure, TLS errors, connection refused/reset, or ctx deadline (TimeoutSeconds=30) exceeded.","commonSituations":"xcili.net is down, blocked, or geo-restricted from the server running pansou; no outbound internet or broken DNS in a container; corporate firewall/proxy blocking the domain; slow site responses exceeding the 30s timeout repeatedly.","solutions":["Check the wrapped error text to distinguish timeout vs connection-refused vs DNS failure","Verify the host can reach https://xcili.net (curl -v from the same machine/container)","Increase TimeoutSeconds or MaxRetries if the site is merely slow","Configure a proxy for restricted networks (HTTP_PROXY/HTTPS_PROXY) or use a mirror","If the site is permanently dead, disable the wuji plugin"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", \"xcili.net:443\", 5*time.Second)\nif err != nil {\n    return fmt.Errorf(\"wuji upstream unreachable: %w\", err)\n}\nconn.Close()","typeGuard":null,"tryCatchPattern":"results, err := p.Search(keyword, ext)\nif err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        // retry with backoff or fall back to another plugin\n    } else if strings.Contains(err.Error(), \"搜索请求失败\") {\n        log.Printf(\"wuji transport error: %v\", err)\n    }\n}","preventionTips":["Treat wuji as one source among many — degrade gracefully instead of failing the whole search","Ensure the deployment has outbound HTTPS and working DNS","Increase TimeoutSeconds/MaxRetries for slow networks","Configure proxy env vars when the site is geo-blocked"],"tags":["network","http","timeout","dns","go"],"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"}