xpzouying/xiaohongshu-mcp · error

failed to read image data

Error message

failed to read image data

What it means

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.

Source

Thrown at pkg/downloader/images.go:76

	if parsedURL != nil {
		req.Header.Set("Referer", fmt.Sprintf("%s://%s/", parsedURL.Scheme, parsedURL.Host))
	}

	// 下载图片数据
	resp, err := d.httpClient.Do(req)
	if err != nil {
		return "", errors.Wrapf(err, "failed to download image from %s", imageURL)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("download failed with status %d for URL: %s", resp.StatusCode, imageURL)
	}

	// 读取图片数据
	imageData, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", errors.Wrap(err, "failed to read image data")
	}

	// 检测图片格式
	kind, err := filetype.Match(imageData)
	if err != nil {
		return "", errors.Wrap(err, "failed to detect file type")
	}

	if !filetype.IsImage(imageData) {
		return "", errors.New("downloaded file is not a valid image")
	}

	// 生成唯一文件名
	fileName := d.generateFileName(imageURL, kind.Extension)
	filePath := filepath.Join(d.savePath, fileName)

	// 如果文件已存在,直接返回路径
	if _, err := os.Stat(filePath); err == nil {

View on GitHub (pinned to 332d196854)

Solutions

  1. Retry the download — transient connection resets usually succeed on a second attempt.
  2. 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.
  3. Check for proxy interference and disable/reconfigure it.
  4. Add per-image retry with backoff in the calling loop (DownloadImages continues on error, so failed URLs are listed in its aggregate error).

Example fix

// before
for _, u := range urls {
    p, err := dl.DownloadImage(u)
    ...
}
// after
for _, u := range urls {
    var p string
    err := retry.Do(3, func() error {
        pp, e := dl.DownloadImage(u)
        p = pp
        return e
    })
    ...
}
Defensive patterns

Strategy: retry

Try / catch

path, err := dl.DownloadImage(imgURL)
if err != nil && strings.Contains(err.Error(), "failed to read image data") {
    // body stream cut mid-transfer — retry once or twice
    return retryWithBackoff(imgURL)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05). Data as JSON: /api/errors/e3d666400aa911ed. Report an issue: GitHub.