{"record":{"id":"136bd28eef97798a","repo":"fish2018/pansou","slug":"s-w-136bd2","errorCode":null,"errorMessage":"[%s] 读取响应体失败: %w","messagePattern":"\\[(.+?)\\] 读取响应体失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xys/xys.go","lineNumber":274,"sourceCode":"\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Referer\", BaseURL+\"/\")\n\treq.Header.Set(\"Origin\", BaseURL)\n\treq.Header.Set(\"X-Requested-With\", \"XMLHttpRequest\")\n\n\tresp, err := p.doRequestWithRetry(req, client)\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\trespBody, 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 searchResp SearchResponse\n\tif err := json.Unmarshal(respBody, &searchResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n\t}\n\n\tif searchResp.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索API返回错误: %s\", p.Name(), searchResp.Msg)\n\t}\n\n\tif p.debugMode {\n\t\tlog.Printf(\"[XYS] 搜索API响应成功，data长度: %d\", len(searchResp.Data))\n\t}\n\n\t// 解析HTML内容\n\treturn p.parseSearchResults(searchResp.Data, keyword)","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xys/xys.go#L256-L292","documentation":"executeSearch wraps the error from io.ReadAll when reading the search response body fails. The status was 200 but the body stream could not be fully consumed — typically a mid-stream connection reset/timeout or the 30s context expiring while the body was still streaming.","triggerScenarios":"io.ReadAll(resp.Body) returns err after a 200 status — connection dropped mid-body, context deadline (30s) exceeded during read, or chunked encoding truncated by a proxy.","commonSituations":"Slow/large responses exceeding the 30s deadline; flaky mobile/proxied networks; middleboxes resetting long-lived responses; server closing connections prematurely under load.","solutions":["Retry the request — this is usually transient; doRequestWithRetry already wraps the send but the body read is outside it, so add read-phase retry in executeSearch","Increase the 30s context timeout so large/slow bodies can finish streaming","Log the partial bytes read and the wrapped error to distinguish reset vs deadline exceeded","Check proxy/middlebox behavior if resets occur consistently on large responses"],"exampleFix":"// before\nrespBody, err := io.ReadAll(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 读取响应体失败: %w\", p.Name(), err)\n}\n// after\nerr = context.Cause(ctx) // check if the deadline caused the read failure\nrespBody, err := io.ReadAll(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 读取响应体失败 (读取了 %d 字节): %w\", p.Name(), len(respBody), err)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"respBody, err := io.ReadAll(resp.Body)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.ErrUnexpectedEOF) {\n        return retryOnce()\n    }\n    return nil, fmt.Errorf(\"body read failed: %w\", err)\n}","preventionTips":["Include the body-read inside the retry wrapper, not only the request send","Set the context timeout with headroom for body download, not just connection","Avoid reading bodies over flaky proxies; prefer hosts with stable connectivity"],"tags":["io","http","network","timeout"],"backgroundTag":"file-read-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"}