{"record":{"id":"a83b3869dff83ce0","repo":"xpzouying/xiaohongshu-mcp","slug":"failed-to-download-image-from-s","errorCode":null,"errorMessage":"failed to download image from %s","messagePattern":"failed to download image from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/downloader/images.go","lineNumber":65,"sourceCode":"\t// 创建请求并设置请求头\n\treq, err := http.NewRequest(\"GET\", imageURL, nil)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to create request\")\n\t}\n\n\t// 设置 User-Agent，模拟浏览器请求\n\treq.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\")\n\n\t// 设置 Referer，使用图片 URL 的域名\n\tparsedURL, _ := url.Parse(imageURL)\n\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}","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/pkg/downloader/images.go#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check errors.Is(err, context.DeadlineExceeded) / os.IsTimeout on the wrapped error — if timeout, retry or raise the client timeout.","Verify the URL host still resolves (dig/nslookup) — expired XHS CDN tokens can kill the host route; re-fetch fresh URLs.","Check proxy/VPN/firewall settings (HTTP_PROXY/HTTPS_PROXY env vars) if behind a corporate network.","Implement per-URL retry with backoff; DownloadImages already isolates per-URL failures and reports them at the end."],"exampleFix":"// before\npath, err := dl.DownloadImage(imgURL)\nif err != nil { return err }\n// after\nvar path string\nerr := retry.Do(3, func() error {\n    p, e := dl.DownloadImage(imgURL)\n    if e != nil && errors.Is(e, context.DeadlineExceeded) {\n        return e // retry\n    }\n    path, err = p, e\n    return nil\n})","handlingStrategy":"retry","validationCode":"if !IsImageURL(imgURL) {\n    return fmt.Errorf(\"skip non-http url\")\n}\n// optionally pre-check reachability\nresp, err := http.Head(imgURL)\nif err != nil || resp.StatusCode != 200 { /* skip or refresh URL */ }","typeGuard":null,"tryCatchPattern":"path, err := dl.DownloadImage(imgURL)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || os.IsTimeout(err) {\n        return retryWithBackoff(imgURL)\n    }\n    return fmt.Errorf(\"transport failed for %s: %w\", imgURL, err)\n}","preventionTips":["Use fresh CDN URLs — expired XHS tokens often break routes.","Set HTTP(S)_PROXY correctly in restricted networks.","Retry with exponential backoff for transient transport errors.","Check DNS/firewall if failures cluster on one host."],"tags":["network","http-client","timeout","downloader"],"backgroundTag":"http-request-failed","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}