{"record":{"id":"0407728db9203868","repo":"fish2018/pansou","slug":"d-w-040772","errorCode":null,"errorMessage":"重试 %d 次后失败: %w","messagePattern":"重试 (.+?) 次后失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/lingjisp/lingjisp.go","lineNumber":286,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif resp != nil {\n\t\t\t\tresp.Body.Close()\n\t\t\t}\n\t\t\tlastErr = err\n\t\t\tif lastErr == nil {\n\t\t\t\tlastErr = fmt.Errorf(\"HTTP %d\", resp.StatusCode)\n\t\t\t}\n\t\t}\n\t\tcancel()\n\n\t\tif attempt < lingjiMaxRetries-1 {\n\t\t\ttime.Sleep(200 * time.Millisecond * time.Duration(1<<attempt))\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"重试 %d 次后失败: %w\", lingjiMaxRetries, lastErr)\n}\n\nfunc dedupeLingjiItems(items []lingjiVideoItem) []lingjiVideoItem {\n\tseen := make(map[int]struct{})\n\tresults := make([]lingjiVideoItem, 0, len(items))\n\n\tfor _, item := range items {\n\t\tid := chooseLingjiID(item)\n\t\tif id == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[id]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[id] = struct{}{}\n\t\tresults = append(results, item)\n\t}\n\treturn results","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/lingjisp/lingjisp.go#L268-L304","documentation":"doLingjiGET retries an HTTP GET against the LingJi API up to lingjiMaxRetries with exponential backoff. When every attempt fails it aborts the search/detail fetch and wraps the last underlying error with this message. It signals a persistent upstream/network failure, not a caller mistake.","triggerScenarios":"All lingjiMaxRetries HTTP attempts fail (connection error, timeout, non-200) inside doLingjiGET, invoked from fetchSearchItems or fetchDetail; the final error is the wrapped lastErr.","commonSituations":"LingJi API is down or rate-limiting the host; no outbound network/DNS in the container; the endpoint URL changed; TLS/proxy issues; upstream returning 5xx on every retry.","solutions":["Check network egress and DNS from the deployment environment (curl the LingJi endpoint)","Inspect the wrapped lastErr (via errors.Unwrap/%v) to identify the actual cause","Verify the LingJi base URL is current; update if the upstream moved","Increase lingjiMaxRetries or backoff if failures are transient under load","If upstream is rate-limiting, add client-side rate limiting or caching"],"exampleFix":"// before\nitems, err := doLingjiGET(url)\nif err != nil { return err }\n// after\nitems, err := doLingjiGET(url)\nif err != nil {\n    log.Printf(\"lingji upstream unavailable: %v\", err)\n    return model.ErrUpstreamUnavailable\n}","handlingStrategy":"retry","validationCode":"// preflight\nresp, err := http.Head(lingjiBaseURL + \"/health\")\nif err != nil || resp.StatusCode != 200 { skipPlugin(\"lingji unreachable\") }","typeGuard":null,"tryCatchPattern":"items, err := doLingjiGET(url)\nif err != nil {\n    log.Printf(\"lingji failed after retries: %v\", err)\n    return fallbackPlugin.Search(kw) // degrade gracefully\n}","preventionTips":["Health-check the upstream before enabling the plugin","Always inspect the wrapped lastErr, not just the wrapper message","Cache successful responses to reduce upstream dependence"],"tags":["network","retry","http","upstream"],"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"}