xpzouying/xiaohongshu-mcp · error

failed to download image from %s

Error message

failed to download image from %s

What it means

DownloadImage wraps an httpClient.Do transport failure (pkg/downloader/images.go:65), including the client's 30-second timeout. The error means the request never completed: DNS failure, TLS error, connection refused/reset, redirect loop, or context/deadline exceeded. The URL is embedded in the message via Wrapf.

Source

Thrown at pkg/downloader/images.go:65

	// 创建请求并设置请求头
	req, err := http.NewRequest("GET", imageURL, nil)
	if err != nil {
		return "", errors.Wrap(err, "failed to create request")
	}

	// 设置 User-Agent,模拟浏览器请求
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")

	// 设置 Referer,使用图片 URL 的域名
	parsedURL, _ := url.Parse(imageURL)
	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")
	}

View on GitHub (pinned to 332d196854)

Solutions

  1. Check errors.Is(err, context.DeadlineExceeded) / os.IsTimeout on the wrapped error — if timeout, retry or raise the client timeout.
  2. Verify the URL host still resolves (dig/nslookup) — expired XHS CDN tokens can kill the host route; re-fetch fresh URLs.
  3. Check proxy/VPN/firewall settings (HTTP_PROXY/HTTPS_PROXY env vars) if behind a corporate network.
  4. Implement per-URL retry with backoff; DownloadImages already isolates per-URL failures and reports them at the end.

Example fix

// before
path, err := dl.DownloadImage(imgURL)
if err != nil { return err }
// after
var path string
err := retry.Do(3, func() error {
    p, e := dl.DownloadImage(imgURL)
    if e != nil && errors.Is(e, context.DeadlineExceeded) {
        return e // retry
    }
    path, err = p, e
    return nil
})
Defensive patterns

Strategy: retry

Validate before calling

if !IsImageURL(imgURL) {
    return fmt.Errorf("skip non-http url")
}
// optionally pre-check reachability
resp, err := http.Head(imgURL)
if err != nil || resp.StatusCode != 200 { /* skip or refresh URL */ }

Try / catch

path, err := dl.DownloadImage(imgURL)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || os.IsTimeout(err) {
        return retryWithBackoff(imgURL)
    }
    return fmt.Errorf("transport failed for %s: %w", imgURL, err)
}

Prevention

When it happens

Trigger: Calling DownloadImage/DownloadImages when the image CDN is unreachable: DNS resolution fails, the host refuses connections, TLS handshake fails, network drops mid-request, or the 30s http.Client timeout expires.

Common situations: Expired CDN URLs that no longer resolve or get refused; offline/air-gapped environments; corporate proxy interference; very large images on slow links hitting the 30s timeout; rate-limiting causing connection resets.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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