You open Google Merchant Center diagnostics and half your catalog has the same red flag:
Issue: Generic image link issue [image_link_internal_error]
Attribute: image link [image_link]
Or, in the older Diagnostics UI:
Generic image link issue
Image link [image_link]: There was a problem when fetching this image.
The error message is unhelpful on purpose. "Internal error" sounds like Google's problem, so most merchants click "Resubmit" once or twice, see no change, and move on. The products keep getting disapproved, and Shopping ad coverage quietly tanks.
image_link_internal_error does sometimes mean something is wrong on Google's side — but in practice, 80 percent of the time the merchant's CDN, image URL, or feed-side image transformation is the cause. This post walks through what the error actually signals, why Google issues it, and the five fixes ranked by how often each one is the real culprit.
What image_link_internal_error actually means
Google's product data spec requires a usable image at the URL provided in the image_link attribute. When Google's image fetcher tries to download that image and something goes wrong on Google's end during fetching, processing, or storage, you see image_link_internal_error.
The key word is "internal." This is not the same as image_link_broken (the URL returns a 404) or image_link_url_problem (the URL is malformed). image_link_internal_error means Google's pipeline got far enough to start fetching the image, but the download or post-processing step failed in a way Google chose not to expose to you.
The vague wording is intentional: it covers everything from a transient timeout on Google's side, to a CDN that returned a 200 with a corrupt body, to an image that decoded but failed Google's content-safety scan. From the merchant's perspective they all look the same.
Why Google flags it
Three categories of problem trigger this error, in order of how often each one is the actual cause:
- The image URL works for browsers but fails for Googlebot-Image. Most common. The URL is reachable, but the response is conditional on a header, cookie, or referrer that Google's fetcher does not send.
- The image is technically reachable but unusable. It returns 200 OK, but the body is corrupt, an HTML error page, or an image format Google rejects (HEIC, WebP without a JPEG fallback, animated GIF on a category that requires a static frame).
- A real transient failure on Google's image pipeline. Less common than the docs imply. Roughly 5 to 10 percent of cases. These usually self-resolve within 24 to 48 hours after the next crawl.
Because Google retries automatically, the third category usually clears itself before you'd notice. If you see image_link_internal_error persisting across multiple Diagnostics refreshes, the cause is almost always category 1 or 2.
Fixes ranked by frequency
Fix 1: Verify the URL works for Googlebot-Image, not just for you
Open a terminal and try the exact URL Google sees:
curl -I -A "Googlebot-Image/1.0" -L "https://cdn.example.com/products/sku-123.jpg"
You want a single 200 response with a Content-Type: image/jpeg (or image/png, image/webp) and a Content-Length greater than a few KB.
What goes wrong here:
- Bot blocking on the CDN. Cloudflare, Fastly, and AWS CloudFront all let you block by user-agent. Some Shopify apps and headless setups ship with overly aggressive rules that block
Googleboton the assumption that "Googlebot" only crawls HTML. Google's image fetcher usesGooglebot-Image/1.0, and many WAF rules forget to allowlist it separately. - Hotlink protection. If your CDN requires a
Refererheader pointing at your domain, Googlebot-Image will fail because it doesn't send referrers. - Geo-restricted images. Some merchants restrict image access by region. Google's fetcher comes from US IPs.
- Cookie-gated CDNs. Signed-URL setups that expire or require a session cookie will fail every fetch after the cookie expires.
The fix is the same in all four cases: explicitly allow Googlebot, Googlebot-Image, and AdsBot-Google on your CDN, regardless of geo or referrer. Google publishes the verifiable IP ranges for its crawlers if you want to allowlist by IP instead of by user-agent.
Fix 2: Check your image URLs are not behind redirect chains
Google's image fetcher follows redirects, but each hop adds latency and increases the chance of timeout on slow CDNs. A 3-hop redirect (example.com → www.example.com → cdn.example.com → cdn.example.com/v2/) is enough to cause sporadic image_link_internal_error on a busy fetch queue.
Run this check across a sample of your image URLs:
curl -sIL -A "Googlebot-Image/1.0" "https://your-image-url.jpg" | grep -E "^(HTTP/|Location:)"
If you see more than one HTTP/ line with a 301 or 302, your image is behind a redirect chain. Update the feed to point at the final canonical URL.
This is especially common after a CDN migration or domain change. The old image URLs still work in browsers (because of the redirect), but Google's bulk image-crawl queue does not tolerate them well.
Fix 3: Verify the response is actually an image, not an HTML error page
Some CDNs return HTTP 200 with an HTML error page in the body when the underlying object is missing. From a browser this looks like a broken image icon. From Google's perspective the response is a 200 OK with Content-Type: text/html instead of image/jpeg, which fails internal validation and surfaces as image_link_internal_error.
Test it:
curl -sI "https://cdn.example.com/products/sku-that-might-not-exist.jpg" | grep -i "^content-type:"
If the result is Content-Type: text/html, the URL is broken even though it returns a 200. Common causes: a misconfigured S3 bucket with a custom error page, a CloudFront origin response policy that swallows 404s, an Nginx default error_page directive that serves HTML on missing objects.
The fix is on the CDN: configure 404s to actually return 404, not HTML. In the meantime, scrub your feed for these URLs by checking the Content-Type header on every image link.
Fix 4: Strip query parameters and tokens from image URLs
Image URLs that contain session tokens, auth signatures, or expiring query parameters fail unpredictably. Common culprits:
?v=1234567890cache-busters that change on every page render?token=abc...Cloudinary signed URLs that expire after 1 hour?w=400&h=400&fit=coveron-the-fly transformations from a service like imgix or Cloudinary on a free tier with rate limits
Google fetches images on a delayed queue. Your tokenized URL might be valid when the feed is generated and expired by the time the fetch happens 6 hours later.
The fix is to use stable, unsigned URLs in your feed. If your image pipeline absolutely requires signed URLs, use long expirations (24+ hours minimum, ideally 7 days) and regenerate the feed often enough that no URL is ever close to expiry at fetch time.
For Shopify merchants: use the canonical cdn.shopify.com/s/files/... URL, not a custom transformation URL. Shopify's CDN is well-behaved with Googlebot-Image.
Fix 5: Make sure the image meets Google's format and dimension requirements
Last on the list because it's already covered by other diagnostics, but image_link_internal_error does sometimes overlap with format issues that Google didn't classify cleanly:
- Format: JPEG, PNG, WebP, GIF, BMP, or TIFF only. HEIC images uploaded directly from iPhones will fail.
- Dimensions: Minimum 100 x 100 pixels for non-apparel, 250 x 250 pixels for apparel. Google's image requirements list the full spec.
- File size: Maximum 16 MB.
- Animated GIFs: Not allowed in some categories. Use a static frame.
If your image_link_internal_error is concentrated on a specific subset of products (one collection, one supplier feed, one variant generation script), it's worth manually inspecting a few images for format issues.
Tools to verify the fix
After making changes, validate before resubmitting.
1. Test a single image URL from Google's perspective. Use Merchant Center's "Test image" feature in the product detail view. It runs the same fetch Google's pipeline runs and surfaces the underlying error, not just the cleaned-up image_link_internal_error.
2. Force a re-crawl with a feed refresh. Pushing the same feed with no changes will not trigger a re-fetch on Google's side. You need to either change the image_link value (even by appending ?v=2) or wait for Google's automatic recrawl, which runs every 1 to 7 days depending on your account's trust score.
3. Bulk-check image URLs from the command line:
# Adjust to your feed's image URL column
xsv select image_link feed.csv | tail -n +2 | head -100 | \
xargs -I{} -P 10 curl -sI -A "Googlebot-Image/1.0" {} | \
awk '/^HTTP/ || /^Content-Type:/ || /^Content-Length:/'
This sends 10 parallel HEAD requests with the Googlebot-Image user-agent and prints the response code, content type, and size for each. Anything that isn't HTTP/2 200, Content-Type: image/*, and a healthy Content-Length is suspect.
4. Watch the disapproval rate trend. After your fix, track the percentage of products with image_link_internal_error in Diagnostics over the next 5 to 7 days. A real fix shows a clear downward slope. If the trend is flat, the fix didn't address the root cause.
What not to do
- Don't keep clicking "Refresh" or "Re-fetch" on individual products. Each one counts as a feed update and can reset the review clock on healthy products in the same submission. See the resubmit anti-pattern in our pending status post.
- Don't add
?v=cache-busters to every image URL hoping Google re-fetches. It does trigger a re-fetch, but it also marks every product as "updated," which delays the review on items that were healthy before. - Don't switch image hosts on a hunch. Diagnose the failure first. Migrating from Shopify CDN to a custom S3 setup mid-incident usually makes things worse.
How SnowPipe handles this
SnowPipe syncs Shopify, WooCommerce, and BigCommerce catalogs to Google Merchant Center via the Merchant API v1. Two design choices in SnowPipe directly head off the most common image_link_internal_error triggers:
Pre-flight image validation. Before any product reaches the Merchant API, SnowPipe runs a HEAD request against every image_link URL with the Googlebot-Image/1.0 user-agent. URLs that return non-200, non-image content types, or redirect chains are flagged in the SnowPipe UI before the data leaves the platform. This catches CDN bot-blocks, broken signed URLs, and CDN error-page issues without burning a Merchant Center submission cycle.
Stable Shopify CDN URL preference. When syncing from Shopify, SnowPipe defaults to the canonical cdn.shopify.com/s/files/... URL, not the variant-specific transformation URL. This trades some image-size customization for predictable Googlebot fetch success.
You can see image validation results in the Products tab inside any GMC connection in SnowPipe, with deep links to the underlying product page if a fix needs to happen at the Shopify or WooCommerce level.
Summary
image_link_internal_error is not usually Google's problem. Most cases come down to a CDN that blocks Googlebot-Image, a redirect chain that times out, or a 200-OK response whose body is HTML instead of an image. Validate the image URL with curl -A "Googlebot-Image/1.0" from outside your network, eliminate redirect chains, and make sure the Content-Type is actually image/*. Real Google-side failures clear within 48 hours; persistent errors are merchant-side configuration issues with a clear fix path.
Tired of fighting Google Merchant Center image errors one product at a time?
Try SnowPipe free — connect your store and get pre-flight image validation plus accurate Google/Facebook syncs in minutes. Or, book a 15-min demo and I'll walk you through your specific setup.