{"record":{"id":"f11b908fd4466c61","repo":"fish2018/pansou","slug":"status-d","errorCode":null,"errorMessage":"status %d","messagePattern":"status (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/ikantv/ikantv.go","lineNumber":221,"sourceCode":"\n\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\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\n\t\tif resp != nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t\tlastErr = err\n\t\tif lastErr == nil {\n\t\t\tlastErr = fmt.Errorf(\"status %d\", statusCode(resp))\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"重试 %d 次后仍然失败: %w\", maxRetries, lastErr)\n}\n\nfunc statusCode(resp *http.Response) int {\n\tif resp == nil {\n\t\treturn 0\n\t}\n\treturn resp.StatusCode\n}\n\ntype apiResponse struct {\n\tCode    int       `json:\"code\"`\n\tMessage string    `json:\"message\"`\n\tData    []apiItem `json:\"data\"`\n}","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/ikantv/ikantv.go#L203-L239","documentation":"doRequestWithRetry in the ikantv plugin wraps its final failure in a Chinese message '重试 %d 次后仍然失败' (still failed after N retries). Inside the retry loop, when the HTTP call returns a response whose status is not acceptable, the plugin synthesizes a 'status %d' error from statusCode(resp). It signals that all retry attempts of the upstream search request were exhausted without success.","triggerScenarios":"All maxRetries attempts of doRequestWithRetry fail; either every attempt returns a transport error, or every attempt returns a non-OK HTTP status so the loop stores fmt.Errorf(\"status %d\", statusCode(resp)) as lastErr.","commonSituations":"ikantv upstream site is down or returning 5xx; site blocks the scraper with 403/429; network/DNS failures in the deployment environment; timeout values too aggressive so transient slowness exhausts retries.","solutions":["Check the wrapped inner error ('status %d' or the transport error) to see the actual cause before changing code","Verify the ikantv upstream URL is reachable from the deployment (curl the search endpoint)","Increase maxRetries or add backoff between attempts for transient upstream failures","Set a realistic User-Agent/headers so the site does not reject the request","Handle the returned error gracefully in doSearch callers and surface a user-facing 'source unavailable' message"],"exampleFix":"// before\nlastErr = fmt.Errorf(\"status %d\", statusCode(resp))\n// after\nlastErr = fmt.Errorf(\"upstream returned HTTP %d after %d retries\", statusCode(resp), maxRetries)","handlingStrategy":"retry","validationCode":"// Go: preflight check before relying on the source\nresp, err := http.Head(sourceBaseURL)\nif err != nil || resp.StatusCode != http.StatusOK {\n    log.Printf(\"ikantv upstream unhealthy: status=%v err=%v\", statusOf(resp), err)\n}","typeGuard":"func isRetryExhausted(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"重试\")\n}","tryCatchPattern":"results, err := p.doSearch(keyword)\nif err != nil {\n    log.Printf(\"ikantv unavailable: %v\", err)\n    results = []model.SearchResult{} // degrade gracefully\n}","preventionTips":["Add exponential backoff between retry attempts","Monitor upstream availability and alert on repeated retry exhaustion","Keep headers/User-Agent current to avoid bot blocks","Set realistic timeouts so transient slowness does not consume all retries"],"tags":["network","http","retry","go","scraping"],"backgroundTag":"http-error-response","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"}