{"record":{"id":"6b485905a16ea4fe","repo":"fish2018/pansou","slug":"s-w-6b4859","errorCode":null,"errorMessage":"[%s] 读取响应体失败: %w","messagePattern":"\\[(.+?)\\] 读取响应体失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/meitizy/meitizy.go","lineNumber":185,"sourceCode":"\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Origin\", FrontendURL)\n\treq.Header.Set(\"Referer\", FrontendURL+\"/\")\n\n\t// 使用优化的客户端发送请求（带重试）\n\tresp, err := p.doRequestWithRetry(req, p.optimizedClient)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求HTTP状态错误: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取响应体失败: %w\", p.Name(), err)\n\t}\n\n\t// 解析JSON响应\n\tvar apiResp searchResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n\t}\n\n\t// 转换为标准格式\n\tresults := p.convertToSearchResults(apiResp.Data)\n\n\t// 关键词过滤（标准网盘插件需要过滤）\n\tfilteredResults := plugin.FilterResultsByKeyword(results, keyword)\n\n\treturn filteredResults, nil\n}\n\n// convertToSearchResults 将API响应转换为标准SearchResult格式","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/meitizy/meitizy.go#L167-L203","documentation":"searchImpl reads the entire response body with io.ReadAll after a 200 status. This error wraps a read failure, which happens when the connection is broken mid-body: server closes the connection prematurely, keep-alive idle connection was reused after it died, TLS error mid-stream, or a proxy truncates the response.","triggerScenarios":"io.ReadAll fails while draining resp.Body: unexpected EOF from a dropped connection, connection reset by peer mid-transfer, or TLS record errors on a reused connection.","commonSituations":"Flaky mobile/proxy networks; server or load balancer timeouts cutting large responses; aggressive keep-alive reuse hitting a server-side idle timeout; unstable container networking.","solutions":["Log the wrapped %w error to confirm premature-close vs TLS issue","Rely on doRequestWithRetry: ensure read errors also trigger a retry (retry before reading the body, or treat read failure as retryable)","Disable keep-alive reuse issues by tuning the transport's IdleConnTimeout or forcing a fresh connection on retry","Check for intermediary proxies/LBs with short response timeouts"],"exampleFix":"// before\nbody, err := io.ReadAll(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 读取响应体失败: %w\", p.Name(), err)\n}\n// after\nbody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 读取响应体失败(可重试): %w\", p.Name(), err)\n}","handlingStrategy":"retry","validationCode":"// declare expected size upfront and cap the read:\nbody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET) {\n        // transient mid-body disconnect: safe to retry the whole request\n        return retrySearch()\n    }\n    return nil, err\n}","preventionTips":["Tune Transport IdleConnTimeout below the server's keep-alive timeout to avoid stale reused connections","Retry the full request when body reads fail — responses were not consumed","Use LimitReader to bound memory and avoid gigantic truncated reads","Check intermediary proxies for short response timeouts"],"tags":["network","io","http-body"],"backgroundTag":"broken-pipe","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"}