{"record":{"id":"f68747450384b450","repo":"fish2018/pansou","slug":"d","errorCode":null,"errorMessage":"重试 %d 次后仍然失败","messagePattern":"重试 (.+?) 次后仍然失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/ash/ash.go","lineNumber":307,"sourceCode":"\t\t\n\t\t// 清理响应\n\t\tif resp != nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t\t\n\t\tlastErr = err\n\t\t\n\t\t// 如果是上下文取消或超时，不再重试\n\t\tif req.Context().Err() != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\t\n\tif lastErr != nil {\n\t\treturn nil, fmt.Errorf(\"重试 %d 次后仍然失败: %w\", maxRetries, lastErr)\n\t}\n\t\n\treturn nil, fmt.Errorf(\"重试 %d 次后仍然失败\", maxRetries)\n}\n\n","sourceCodeStart":289,"sourceCodeEnd":310,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/ash/ash.go#L289-L310","documentation":"Companion to error 37: when the retry loop in ash's doRequestWithRetry ends with lastErr == nil — which happens when the loop broke early because req.Context().Err() != nil (context cancelled or deadline exceeded) — the function still must return an error, so it returns '重试 %d 次后仍然失败' without a wrapped cause.","triggerScenarios":"The 15-second context from searchImpl expires (or is cancelled) while attempts are still failing/ongoing, so the loop breaks via the ctx.Err() check with no lastErr recorded — ash.go:307.","commonSituations":"Slow upstream site that cannot answer within 15s under current backoff; caller cancelled the search; environment with high latency to the target region.","solutions":["Increase the 15s context timeout in searchImpl to accommodate total backoff + slow responses.","Check req.Context().Err() and include it in the error so the cause is not lost.","Reduce per-attempt cost (fewer header round-trips, HTTP/2 keep-alive) so the deadline suffices.","Distinguish cancellation from timeout in callers to skip pointless re-wrapping."],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"重试 %d 次后仍然失败\", maxRetries)\n// after\nif ctxErr := req.Context().Err(); ctxErr != nil {\n    return nil, fmt.Errorf(\"重试 %d 次后仍然失败: %w\", maxRetries, ctxErr)\n}\nreturn nil, fmt.Errorf(\"重试 %d 次后仍然失败\", maxRetries)","handlingStrategy":"retry","validationCode":"// ensure budget fits: totalBackoff + attempts*perReqTimeout < ctxDeadline\nif maxRetries*retryDelay >= 15*time.Second { reduce retries or raise deadline }","typeGuard":null,"tryCatchPattern":"if err != nil && errors.Is(err, context.DeadlineExceeded) {\n    // treat as timeout: increase deadline or reduce work per attempt\n    return partialResults, err\n}","preventionTips":["Size context deadlines to exceed worst-case retry totals","Propagate cancellation intent so timeout vs cancel is distinguishable","Reduce backoff ceiling when contexts are short","Include ctx.Err() in the final error for diagnosability"],"tags":["network","http","timeout","retry-exhausted","go"],"backgroundTag":"request-timeout","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"}