{"record":{"id":"432cc55eb7d823d0","repo":"fish2018/pansou","slug":"d-w-432cc5","errorCode":null,"errorMessage":"重试 %d 次后失败: %w","messagePattern":"重试 (.+?) 次后失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/alupan/alupan.go","lineNumber":401,"sourceCode":"func (p *AlupanPlugin) doRequestWithRetry(req *http.Request, client *http.Client, maxRetries int) (*http.Response, error) {\n\tvar lastErr error\n\n\tfor attempt := 0; attempt < maxRetries; attempt++ {\n\t\tresp, err := client.Do(req.Clone(req.Context()))\n\t\tif err == nil && resp.StatusCode == http.StatusOK {\n\t\t\treturn resp, nil\n\t\t}\n\t\tif resp != nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t\tlastErr = err\n\t\tif attempt < maxRetries-1 {\n\t\t\tbackoff := retryBaseDelay * time.Duration(1<<attempt)\n\t\t\ttime.Sleep(backoff)\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"重试 %d 次后失败: %w\", maxRetries, lastErr)\n}\n\nfunc startCacheCleaner() {\n\tticker := time.NewTicker(cacheCleanupInterval)\n\tdefer ticker.Stop()\n\n\tfor range ticker.C {\n\t\tnow := time.Now()\n\t\tdetailCache.Range(func(key, value interface{}) bool {\n\t\t\tentry, ok := value.(cacheEntry)\n\t\t\tif !ok || now.After(entry.expiresAt) {\n\t\t\t\tdetailCache.Delete(key)\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n}\n","sourceCodeStart":383,"sourceCodeEnd":419,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/alupan/alupan.go#L383-L419","documentation":"alupan's doRequestWithRetry attempts an HTTP request up to maxRetries times with exponential backoff (retryBaseDelay=200ms doubling per attempt). If every attempt fails, it returns the final wrapped error '重试 %d 次后失败: %w' so callers (searchImpl, fetchDetailLinks) know the retries were exhausted, preserving the underlying cause via %w.","triggerScenarios":"All maxRetries attempts of the HTTP request inside doRequestWithRetry fail — e.g. the alupan search or detail endpoint is unreachable, times out, or the transport keeps returning non-recoverable errors; called from searchImpl (alupan.go:131) or fetchDetailLinks (alupan.go:261).","commonSituations":"Target site is down or blocking (403/429), DNS failure, no network access, TLS errors, or a proxy misconfiguration making every retry fail within the backoff window.","solutions":["Inspect the wrapped lastErr with errors.Unwrap/errors.As to see the true root cause (timeout, connection refused, status code).","Verify network connectivity and that the target host is reachable from the environment (curl the endpoint).","Check for rate limiting/IP blocking on the upstream site and raise retryBaseDelay or maxRetries accordingly.","Add a timeout-aware http.Client and, if the site requires it, valid cookies/headers set before the request."],"exampleFix":"// before\nresults, err := p.searchImpl(keyword)\nif err != nil { return nil, err }\n// after\nresults, err := p.searchImpl(keyword)\nif err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        return nil, fmt.Errorf(\"upstream timed out: %w\", err)\n    }\n    return nil, err // surface wrapped cause for diagnosis\n}","handlingStrategy":"retry","validationCode":"func isRetryable(err error) bool { var ne net.Error; return errors.As(err, &ne) || errors.Is(err, context.DeadlineExceeded) }","typeGuard":null,"tryCatchPattern":"res, err := plugin.Search(keyword)\nif err != nil {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() {\n        // schedule retry with longer deadline\n    } else {\n        log.Printf(\"search failed permanently: %v\", err)\n    }\n}","preventionTips":["Health-check the upstream host before batch searches","Tune retryBaseDelay/maxRetries to the site's actual latency","Monitor for 429/403 rates and back off proactively","Always inspect the wrapped cause with errors.As/Is"],"tags":["network","http","retry-exhausted","go"],"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"}