{"record":{"id":"c06d4d96546aa827","repo":"fish2018/pansou","slug":"s-d-w-c06d4d","errorCode":null,"errorMessage":"[%s] 第%d页读取响应失败: %w","messagePattern":"\\[(.+?)\\] 第(.+?)页读取响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/dy4k/dy4k.go","lineNumber":421,"sourceCode":"\t\t\t}\n\t\t}\n\t\treturn nil, 0, fmt.Errorf(\"[%s] 第%d页搜索请求失败: %w\", p.Name(), page, err)\n\t}\n\tdefer resp.Body.Close()\n\n\tdebugPrintf(\"✅ [Dy4k DEBUG] HTTP请求成功 (耗时: %v)\\n\", requestDuration)\n\n\t// 6. 检查状态码\n\tdebugPrintf(\"🔧 [Dy4k DEBUG] HTTP响应状态码: %d\\n\", resp.StatusCode)\n\tif resp.StatusCode != 200 {\n\t\tdebugPrintf(\"❌ [Dy4k DEBUG] 状态码异常: %d\\n\", resp.StatusCode)\n\t\treturn nil, 0, fmt.Errorf(\"[%s] 第%d页请求返回状态码: %d\", p.Name(), page, resp.StatusCode)\n\t}\n\n\t// 7. 读取并打印HTML响应\n\thtmlBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"[%s] 第%d页读取响应失败: %w\", p.Name(), page, err)\n\t}\n\n\thtmlContent := string(htmlBytes)\n\tdebugPrintf(\"🔧 [Dy4k DEBUG] 第%d页 HTML长度: %d bytes\\n\", page, len(htmlContent))\n\n\t// 保存HTML到文件（仅在调试模式下）\n\tif DebugMode {\n\t\thtmlDir := \"./html\"\n\t\tos.MkdirAll(htmlDir, 0755)\n\n\t\tfilename := fmt.Sprintf(\"dy4k_page_%d_%s.html\", page, strings.ReplaceAll(encodedKeyword, \"%\", \"_\"))\n\t\tfilepath := filepath.Join(htmlDir, filename)\n\n\t\terr = os.WriteFile(filepath, htmlBytes, 0644)\n\t\tif err != nil {\n\t\t\tdebugPrintf(\"❌ [Dy4k DEBUG] 保存HTML文件失败: %v\\n\", err)\n\t\t} else {\n\t\t\tdebugPrintf(\"✅ [Dy4k DEBUG] HTML已保存到: %s\\n\", filepath)","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/dy4k/dy4k.go#L403-L439","documentation":"This error is thrown by Dy4kPlugin.searchPage when io.ReadAll fails while reading the HTTP response body after a search request. It means the connection returned status 200 but the body could not be fully read, typically because the remote server or a proxy closed/reset the connection mid-transfer. The plugin wraps the underlying io error with the plugin name and page number for diagnostics.","triggerScenarios":"Calling Search (via searchImpl -> searchPage) when the 4KDY server accepts the request but drops the TCP connection or times out mid-body during io.ReadAll(resp.Body) for a given search page.","commonSituations":"Unstable network or VPN connections to the target site; server-side anti-scraping that aborts large responses; proxies/CDNs terminating long responses; site under load returning truncated bodies.","solutions":["Retry the request — doRequestWithRetry already retries, but re-running the whole search after a pause usually succeeds","Verify network connectivity/proxy settings to the 4KDY host (curl the same search URL)","Check whether the site now blocks the client User-Agent and rotate UA via getRandomUA","Reduce page size/scope of search pages requested to shrink the response body"],"exampleFix":"// before\nhtmlBytes, err := io.ReadAll(resp.Body)\nif err != nil {\n    return nil, 0, fmt.Errorf(\"[%s] 第%d页读取响应失败: %w\", p.Name(), page, err)\n}\n// after\nhtmlBytes, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize))\nif err != nil {\n    return nil, 0, fmt.Errorf(\"[%s] 第%d页读取响应失败: %w\", p.Name(), page, err)\n}","handlingStrategy":"retry","validationCode":"resp, err := client.Get(url)\nif err != nil { return err }\nif resp.StatusCode != http.StatusOK { return fmt.Errorf(\"bad status: %d\", resp.StatusCode) }\nif resp.Body == nil { return errors.New(\"empty response body\") }","typeGuard":null,"tryCatchPattern":"htmlBytes, err := io.ReadAll(resp.Body)\nif err != nil {\n    return fmt.Errorf(\"read response failed: %w\", err) // caller retries with backoff\n}","preventionTips":["Set reasonable timeouts on the http.Client so half-open connections don't hang","Retry transient body-read errors with backoff","Use io.LimitReader to cap body size on untrusted servers","Monitor network stability / avoid flaky proxies when scraping"],"tags":["network","http","io","go"],"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"}