{"record":{"id":"30a876df14357b9a","repo":"fish2018/pansou","slug":"s-d-w-30a876","errorCode":null,"errorMessage":"[%s] 重试 %d 次后仍然失败: %w","messagePattern":"\\[(.+?)\\] 重试 (.+?) 次后仍然失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xys/xys.go","lineNumber":233,"sourceCode":"\t\t\tbackoff := time.Duration(1<<uint(i-1)) * 200 * time.Millisecond\n\t\t\ttime.Sleep(backoff)\n\t\t}\n\t\t\n\t\t// 克隆请求避免并发问题\n\t\treqClone := req.Clone(req.Context())\n\t\t\n\t\tresp, err := client.Do(reqClone)\n\t\tif err == nil && resp.StatusCode == 200 {\n\t\t\treturn resp, nil\n\t\t}\n\t\t\n\t\tif resp != nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t\tlastErr = err\n\t}\n\t\n\treturn nil, fmt.Errorf(\"[%s] 重试 %d 次后仍然失败: %w\", p.Name(), maxRetries, lastErr)\n}\n\n// executeSearch 执行搜索请求\nfunc (p *XysPlugin) executeSearch(client *http.Client, token, keyword string) ([]model.SearchResult, error) {\n\t// 构建搜索URL\n\tsearchURL := fmt.Sprintf(\"%s%s?DToken2=%s&requestID=undefined&mode=90002&stype=undefined&scope_content=0&wd=%s&uk=&page=1&limit=20&screen_filetype=\",\n\t\tBaseURL, SearchPath, token, 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, \"POST\", searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建搜索请求失败: %w\", p.Name(), err)\n\t}\n\n\t// 设置完整的请求头","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xys/xys.go#L215-L251","documentation":"doRequestWithRetry returns this after its final retry attempt also failed; lastErr is the error from the last attempt. Both the token fetch and the search request go through this helper, so any persistent network problem against the upstream site surfaces as this wrapped error. It signals the transport is unusable, not an HTTP-level status problem.","triggerScenarios":"All maxRetries attempts in the loop returned err (dial timeout, connection refused/reset, TLS errors, context deadline exceeded) and the loop exits, wrapping lastErr.","commonSituations":"Site unreachable from the user's network/region; firewall or GFW blocking; DNS failure; the 30s per-request timeout expiring on a slow upstream; server-side rate limiting resetting connections.","solutions":["Unwrap lastErr to identify the root cause (timeout vs refused vs TLS) and fix accordingly","Test reachability with curl from the deployment host; configure proxy env vars if needed","Increase maxRetries or add exponential backoff for transient failures","Increase the 30s request timeout if the upstream is consistently slow","Retry the whole operation later; if persistent, check upstream site status"],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"[%s] 重试 %d 次后仍然失败: %w\", p.Name(), maxRetries, lastErr)\n// after\nreturn nil, fmt.Errorf(\"[%s] 重试 %d 次后仍然失败 (最后一次: %v): %w\", p.Name(), maxRetries, lastErr, lastErr)","handlingStrategy":"retry","validationCode":"// health probe before search\nresp, err := client.Head(BaseURL)\nif err != nil { return fmt.Errorf(\"upstream unreachable: %w\", err) }\nresp.Body.Close()","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() {\n        return retryAfter(2 * time.Second)\n    }\n    return nil, err // non-retryable transport failure\n}","preventionTips":["Use exponential backoff between retries instead of immediate retry","Verify DNS/proxy/firewall on the deployment host before blaming the library","Cap total request time with an outer context so retries cannot hang callers"],"tags":["network","retry-exhausted","http","timeout"],"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"}