{"record":{"id":"e3d666400aa911ed","repo":"xpzouying/xiaohongshu-mcp","slug":"failed-to-read-image-data","errorCode":null,"errorMessage":"failed to read image data","messagePattern":"failed to read image data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/downloader/images.go","lineNumber":76,"sourceCode":"\tif parsedURL != nil {\n\t\treq.Header.Set(\"Referer\", fmt.Sprintf(\"%s://%s/\", parsedURL.Scheme, parsedURL.Host))\n\t}\n\n\t// 下载图片数据\n\tresp, err := d.httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", errors.Wrapf(err, \"failed to download image from %s\", imageURL)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"download failed with status %d for URL: %s\", resp.StatusCode, imageURL)\n\t}\n\n\t// 读取图片数据\n\timageData, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to read image data\")\n\t}\n\n\t// 检测图片格式\n\tkind, err := filetype.Match(imageData)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to detect file type\")\n\t}\n\n\tif !filetype.IsImage(imageData) {\n\t\treturn \"\", errors.New(\"downloaded file is not a valid image\")\n\t}\n\n\t// 生成唯一文件名\n\tfileName := d.generateFileName(imageURL, kind.Extension)\n\tfilePath := filepath.Join(d.savePath, fileName)\n\n\t// 如果文件已存在，直接返回路径\n\tif _, err := os.Stat(filePath); err == nil {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/pkg/downloader/images.go#L58-L94","documentation":"DownloadImage wraps an io.ReadAll failure on the response body (pkg/downloader/images.go:76). The HTTP response already returned 200, but reading the streamed body failed — typically the connection was closed or reset mid-transfer, or a read deadline elapsed. The partial data is discarded; nothing is saved.","triggerScenarios":"Calling DownloadImage when the server closes the connection mid-body, a proxy/load balancer cuts the stream, or the http.Client's transport hits an I/O error (e.g. 'unexpected EOF', 'connection reset by peer') while draining resp.Body.","commonSituations":"Large images over flaky mobile/proxy networks; CDN edge nodes aborting slow downloads; keep-alive connections reused after an idle drop; aggressive server-side timeouts.","solutions":["Retry the download — transient connection resets usually succeed on a second attempt.","If large files repeatedly truncate, the 30s client timeout may indirectly cut reads; increase NewImageDownloader's client timeout or stream to disk instead of io.ReadAll.","Check for proxy interference and disable/reconfigure it.","Add per-image retry with backoff in the calling loop (DownloadImages continues on error, so failed URLs are listed in its aggregate error)."],"exampleFix":"// before\nfor _, u := range urls {\n    p, err := dl.DownloadImage(u)\n    ...\n}\n// after\nfor _, u := range urls {\n    var p string\n    err := retry.Do(3, func() error {\n        pp, e := dl.DownloadImage(u)\n        p = pp\n        return e\n    })\n    ...\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"path, err := dl.DownloadImage(imgURL)\nif err != nil && strings.Contains(err.Error(), \"failed to read image data\") {\n    // body stream cut mid-transfer — retry once or twice\n    return retryWithBackoff(imgURL)\n}","preventionTips":["Retry aborted body reads — resets are usually transient.","Increase the http.Client timeout for large images or slow links.","Avoid unstable proxies/keep-alive reuse for big transfers.","Aggregate per-URL failures from DownloadImages and retry only those."],"tags":["network","io","http-client","downloader"],"backgroundTag":"connection-reset","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}