{"record":{"id":"7e12f685321e7ee0","repo":"fish2018/pansou","slug":"w-7e12f6","errorCode":null,"errorMessage":"读取响应失败: %w","messagePattern":"读取响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/pan666/pan666.go","lineNumber":219,"sourceCode":"\t// 重试逻辑\n\tfor i := 0; i <= p.retries; i++ {\n\t\t// 发送请求\n\t\tresp, err = client.Do(req)\n\t\tif err != nil {\n\t\t\tif i == p.retries {\n\t\t\t\treturn nil, false, fmt.Errorf(\"请求失败: %w\", err)\n\t\t\t}\n\t\t\ttime.Sleep(500 * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\tdefer resp.Body.Close()\n\t\t\n\t\t// 读取响应体\n\t\tresponseBody, err = io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\tif i == p.retries {\n\t\t\t\treturn nil, false, fmt.Errorf(\"读取响应失败: %w\", err)\n\t\t\t}\n\t\t\ttime.Sleep(500 * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\t// 状态码检查\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tif i == p.retries {\n\t\t\t\treturn nil, false, fmt.Errorf(\"API返回非200状态码: %d\", resp.StatusCode)\n\t\t\t}\n\t\t\ttime.Sleep(500 * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\t// 请求成功，跳出重试循环\n\t\tbreak\n\t}\n\t","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/pan666/pan666.go#L201-L237","documentation":"pan666's fetchPage returns this when io.ReadAll(resp.Body) fails on the last retry. A response was received but the body stream broke mid-read — connection reset, premature close, or transfer interrupted. Earlier attempts retry after 500ms; the final one surfaces the io error wrapped.","triggerScenarios":"After a successful client.Do, reading the body fails with io.ErrUnexpectedEOF, connection reset by peer, or similar — server/proxy killed the connection before the full body arrived, on every retry.","commonSituations":"Unstable upstream or CDN dropping large responses; middlebox truncating keep-alive connections; server under load closing connections early; VPN/proxy link flapping.","solutions":["Retry — this class of error is usually transient","Check network path stability (proxy, VPN, firewall) between client and pan666 API","Confirm the server isn't closing connections early (curl -v, check Content-Length vs received bytes)","Disable aggressive connection reuse/keep-alive tuning if stale sockets cause resets"],"exampleFix":"// before\nresponseBody, err = io.ReadAll(resp.Body)\nif err != nil {\n    if i == p.retries { return nil, false, fmt.Errorf(\"读取响应失败: %w\", err) }\n// after\nresponseBody, err = io.ReadAll(io.LimitReader(resp.Body, 10<<20))\nif err != nil {\n    if errors.Is(err, io.ErrUnexpectedEOF) && i < p.retries { time.Sleep(500 * time.Millisecond); continue }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil {\n    if errors.Is(err, io.ErrUnexpectedEOF) {\n        time.Sleep(1 * time.Second)\n        return fetchPageRetry(offset) // full re-fetch; partial body is unusable\n    }\n    return err\n}","preventionTips":["Never parse partial bodies — always refetch on read error","Use io.LimitReader to cap body size","Prefer fresh connections over reused keep-alive sockets when resets recur","Monitor truncation rates to detect upstream instability"],"tags":["io","http-response","retry","plugin"],"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"}