{"record":{"id":"bb574b91caf111d6","repo":"fish2018/pansou","slug":"s-w-bb574b","errorCode":null,"errorMessage":"[%s] 搜索请求失败: %w","messagePattern":"\\[(.+?)\\] 搜索请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/ikantv/ikantv.go","lineNumber":85,"sourceCode":"\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\n\treq.Header.Set(\"User-Agent\", \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\")\n\treq.Header.Set(\"Accept\", \"application/json, text/plain, */*\")\n\treq.Header.Set(\"Accept-Language\", \"zh-CN,zh;q=0.9,en;q=0.8\")\n\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Referer\", defaultReferer)\n\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\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\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\n\tvar apiResp apiResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n\t}\n\tif apiResp.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"[%s] API错误: %s\", p.Name(), apiResp.Message)","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/ikantv/ikantv.go#L67-L103","documentation":"ikantv plugin's doSearch throws this when the request execution (p.doRequestWithRetry) fails after retries are exhausted, wrapping the last underlying error. It covers connection, DNS, TLS, and timeout failures, plus any retry-exhaustion sentinel from the retry helper.","triggerScenarios":"All attempts in doRequestWithRetry fail: the ikantv API host is unreachable, DNS fails, TLS handshake fails, or the defaultTimeout context deadline expires on each attempt.","commonSituations":"API domain is dead or DNS-poisoned; the site blocks datacenter IPs; timeout too aggressive for the upstream; network outage on the host running the aggregator.","solutions":["Curl the ikantv search URL from the same host to confirm reachability.","Increase defaultTimeout so slow upstream responses don't exhaust every retry attempt.","Inspect the wrapped cause (the %w chain) to distinguish timeout vs connection-refused vs DNS failure.","Check proxy/VPN/firewall settings and set HTTPS_PROXY if the environment routes through one.","Verify the ikantv API domain is still alive — plugin endpoints for such sites often change; update the configured base URL if so."],"exampleFix":"// before\nresp, err := p.doRequestWithRetry(req, client)\nif err != nil {\n\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n}\n// after\nvar netErr net.Error\nif errors.As(err, &netErr) && netErr.Timeout() {\n\treturn nil, fmt.Errorf(\"[%s] 搜索请求超时: %w\", p.Name(), err)\n}\nif err != nil {\n\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n}","handlingStrategy":"retry","validationCode":"// reachability pre-check before search\nu, _ := url.Parse(searchURL)\nconn, err := net.DialTimeout(\"tcp\", net.JoinHostPort(u.Hostname(), portOrDefault(u, \"443\")), 3*time.Second)\nif err != nil {\n\treturn fmt.Errorf(\"ikantv host unreachable: %w\", err)\n}\nconn.Close()","typeGuard":null,"tryCatchPattern":"results, err := p.doSearch(ctx, keyword)\nvar netErr net.Error\nif err != nil && errors.As(err, &netErr) && netErr.Timeout() {\n\t// transient: retry with backoff or extend defaultTimeout\n} else if err != nil {\n\t// permanent network failure: skip this source\n\tlog.Printf(\"ikantv unreachable, skipping: %v\", err)\n}","preventionTips":["Curl the API host from the deployment environment before rollout","Size defaultTimeout and retry counts for the upstream's real latency","Check the wrapped error chain to classify timeout vs DNS vs refused","Re-verify the plugin's domain periodically — aggregator endpoints rot"],"tags":["network","http","go","timeout","retry"],"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"}