{"record":{"id":"2d1a56900f491b8b","repo":"fish2018/pansou","slug":"s-api-w-2d1a56","errorCode":null,"errorMessage":"[%s] 读取 API 响应失败: %w","messagePattern":"\\[(.+?)\\] 读取 API 响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/feikuai/feikuai.go","lineNumber":139,"sourceCode":"\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Referer\", \"https://feikuai.tv/\")\n\n\t// 发送请求（带重试）\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn p.searchWeb(client, keyword, fmt.Errorf(\"[%s] 搜索 API 请求失败: %w\", p.Name(), err))\n\t}\n\tdefer resp.Body.Close()\n\n\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn p.searchWeb(client, keyword, fmt.Errorf(\"[%s] 搜索 API 返回状态码: %d\", p.Name(), resp.StatusCode))\n\t}\n\n\t// 读取并解析JSON响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn p.searchWeb(client, keyword, fmt.Errorf(\"[%s] 读取 API 响应失败: %w\", p.Name(), err))\n\t}\n\n\tvar apiResp FeikuaiAPIResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn p.searchWeb(client, keyword, fmt.Errorf(\"[%s] API JSON 解析失败: %w\", p.Name(), err))\n\t}\n\n\t// 检查API响应状态\n\tif apiResp.Code != 0 {\n\t\treturn p.searchWeb(client, keyword, fmt.Errorf(\"[%s] API 返回错误: %s (code: %d)\", p.Name(), apiResp.Msg, apiResp.Code))\n\t}\n\n\t// 解析搜索结果\n\tvar results []model.SearchResult\n\tfor _, item := range apiResp.Items {\n\t\t// 每个item可能包含多个种子\n\t\tfor _, torrent := range item.Torrents {\n\t\t\tresult := p.parseTorrent(keyword, item, torrent)","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/feikuai/feikuai.go#L121-L157","documentation":"searchImpl reads the entire API response body with io.ReadAll; a failure here (premature connection close, read timeout, truncated chunked encoding) triggers the searchWeb fallback with this wrapped error. The response arrived but its body could not be fully consumed.","triggerScenarios":"io.ReadAll(resp.Body) returns non-nil — connection reset mid-body, context deadline hit while streaming, invalid chunked transfer encoding.","commonSituations":"Flaky upstream connection, server/proxy cutting large responses short, DefaultTimeout expiring during body read, unstable network or VPN.","solutions":["Inspect the wrapped io error (unexpected EOF vs context deadline)","Increase DefaultTimeout if the deadline fires during body read","Retry the request; add the body read inside doRequestWithRetry for retry coverage","Prefer the searchWeb fallback results when API is flaky"],"exampleFix":"// before\nbody, err := io.ReadAll(resp.Body)\nif err != nil {\n    return p.searchWeb(client, keyword, fmt.Errorf(\"[%s] 读取 API 响应失败: %w\", p.Name(), err))\n}\n// after\nbody, err := io.ReadAll(io.LimitReader(resp.Body, 10<<20))\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return p.searchWeb(client, keyword, fmt.Errorf(\"[%s] 读取 API 响应超时: %w\", p.Name(), err))\n    }\n    return p.searchWeb(client, keyword, fmt.Errorf(\"[%s] 读取 API 响应失败: %w\", p.Name(), err))\n}","handlingStrategy":"retry","validationCode":"// check response completeness signals\n// (pre-request there is nothing to validate; ensure client timeouts are set)\nclient := &http.Client{Timeout: 30 * time.Second}","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword)\nif err != nil && strings.Contains(err.Error(), \"读取 API 响应失败\") {\n    if errors.Is(err, context.DeadlineExceeded) {\n        time.Sleep(time.Second)\n        results, err = plugin.Search(keyword) // one bounded retry\n    }\n}","preventionTips":["Give body reads generous timeouts separate from connect timeouts","Use io.LimitReader to avoid pathological huge/truncated bodies","Retry idempotent GETs on mid-body failures","Prefer stable networks/VPN endpoints when scraping geo-blocked sites"],"tags":["http","io","response-body"],"backgroundTag":"network-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"}