{"record":{"id":"404d4384e07e4383","repo":"fish2018/pansou","slug":"s-w-404d43","errorCode":null,"errorMessage":"[%s] 搜索请求失败: %w","messagePattern":"\\[(.+?)\\] 搜索请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/nsgame/nsgame.go","lineNumber":162,"sourceCode":"\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\n\tdefer cancel()\n\n\t// 3. 创建请求\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\n\t// 4. 设置请求头\n\treq.Header.Set(\"User-Agent\", \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 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\tp.setRequestHeaders(req, \"https://nsthwj.cn/\")\n\n\t// 5. 发送请求（带重试机制）\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 != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 6. 读取响应体\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\t// 7. 解析JSON响应\n\tvar apiResp NSGameResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n\t}","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/nsgame/nsgame.go#L144-L180","documentation":"The NSGame plugin wraps any transport-level failure of its GET search request to nsthwj.cn (issued inside doRequestWithRetry) with this message, preserving the underlying error via %w. It fires before any status-code or body inspection, so it always means the HTTP round trip itself failed after the plugin's internal retries were exhausted. The plugin name is prefixed to help identify which aggregator plugin failed.","triggerScenarios":"doRequestWithRetry returns an error: DNS resolution failure for the API host, TCP connect failure, TLS handshake error, context deadline exceeded (defaultTimeout), client.Do transport error, or all retries exhausted on retryable errors.","commonSituations":"Target site is down or blocked in the user's region/network (common with nsthwj.cn), corporate proxy or firewall blocking the request, no internet connectivity, DNS poisoning, or the site added anti-bot measures that reset connections.","solutions":["Verify basic connectivity to nsthwj.cn (curl -v the apiURL) from the host running the app","Check the wrapped cause with errors.Is/errors.As for context.DeadlineExceeded vs DNS vs TLS errors and fix the specific layer (DNS, proxy, timeout)","Increase the timeout or retry count if the site is slow rather than unreachable","Configure HTTP_PROXY/HTTPS_PROXY if the host requires egress via a proxy","If the site is permanently unreachable, disable the NSGame plugin"],"exampleFix":"// before\nresp, err := p.doRequestWithRetry(req, client)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n}\n// after\nresp, err := p.doRequestWithRetry(req, client)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return nil, fmt.Errorf(\"[%s] 搜索请求超时(%v): %w\", p.Name(), defaultTimeout, err)\n    }\n    return nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n}","handlingStrategy":"retry","validationCode":"func canReachNSGame() error {\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n    req, _ := http.NewRequestWithContext(ctx, http.MethodGet, \"https://nsthwj.cn/\", nil)\n    resp, err := http.DefaultClient.Do(req)\n    if err != nil {\n        return err\n    }\n    resp.Body.Close()\n    return nil\n}","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword, ext)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        // timeout: schedule retry with backoff\n    } else {\n        log.Warn(\"nsgame unreachable, skipping plugin\", \"err\", err)\n    }\n    return fallbackResults\n}","preventionTips":["Probe the upstream host with a cheap health check before enabling the plugin","Always inspect the wrapped error with errors.Is/As instead of treating all failures the same","Set realistic timeouts and retry counts for slow upstreams","Run from a network that can reach the target region"],"tags":["network","http","go","upstream-unreachable"],"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"}