{"record":{"id":"61a6fbeae14d1d7a","repo":"fish2018/pansou","slug":"d-w-61a6fb","errorCode":null,"errorMessage":"重试 %d 次后仍然失败: %w","messagePattern":"重试 (.+?) 次后仍然失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/kkv/kkv.go","lineNumber":409,"sourceCode":"\tfor i := 0; i < maxRetries; i++ {\n\t\tif i > 0 {\n\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\treqClone := req.Clone(req.Context())\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(\"重试 %d 次后仍然失败: %w\", maxRetries, lastErr)\n}\n","sourceCodeStart":391,"sourceCodeEnd":411,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/kkv/kkv.go#L391-L411","documentation":"doRequestWithRetry exhausts maxRetries attempts (closing any non-nil response body each time, recording lastErr) and returns a wrapped error with the retry count. This is the aggregate failure of every attempt; the original cause is inside the wrapped lastErr (timeout, connection refused, bad status, etc.).","triggerScenarios":"All maxRetries attempts of the HTTP request fail — every attempt returns a transport error (dial/timeout/TLS) or an error status the retry loop treats as failure, and the loop falls through to the final return.","commonSituations":"Upstream site down for the entire retry window, IP rate-limited or blocked so every attempt fails, DNS dead for a defunct mirror, network egress disabled in the deployment environment.","solutions":["Unwrap lastErr (errors.Unwrap / %w chain) to identify the root cause — timeout vs refused vs status","If context deadline exceeded, increase the request timeout or retry budget","If connection refused/DNS errors, fix or update the source base URL","If rate-limited, add backoff between retries or use a different egress IP/proxy"],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"重试 %d 次后仍然失败: %w\", maxRetries, lastErr)\n// after\nreturn nil, fmt.Errorf(\"重试 %d 次后仍然失败: %w\", maxRetries, lastErr) // caller: errors.Is(err, context.DeadlineExceeded) to branch","handlingStrategy":"retry","validationCode":"if err := net.DialTimeout(\"tcp\", host+\":443\", 5*time.Second); err != nil {\n    // host unreachable; skip before invoking retry loop\n}","typeGuard":"func isRetryExhausted(err error) bool {\n    return strings.Contains(err.Error(), \"重试\") && strings.Contains(err.Error(), \"仍然失败\")\n}","tryCatchPattern":"if err != nil {\n    if isRetryExhausted(err) {\n        root := errors.Unwrap(err) // lastErr: the real cause\n        log.Printf(\"all retries failed, root cause: %v\", root)\n        return fallbackSource.Search(ctx, kw)\n    }\n    return err\n}","preventionTips":["Always unwrap to the root cause before reacting — the outer message only reports the retry count","Use exponential backoff between retries instead of tight loops","Cap total retry budget so a dead source fails fast","Health-check sources and disable ones failing repeatedly"],"tags":["network","http","retry-exhausted","scraping"],"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"}