BerriAI/litellm · error · ValueError
URL does not point to a valid image (content-type: {content_
Error message
URL does not point to a valid image (content-type: {content_type}) What it means
Raised by _load_image_from_url after it fetched the URL successfully (or the response lacked headers): the HTTP response's content-type header is missing or does not contain 'image', so the bytes are not an image. This protects the Pillow path from trying to open HTML error pages and the like.
Source
Thrown at litellm/litellm_core_utils/prompt_templates/factory.py:3190
def _load_image_from_url(image_url):
try:
from PIL import Image
except Exception:
raise Exception("image conversion failed please run `pip install Pillow`")
from io import BytesIO
try:
# Send a GET request to the image URL
client: Final = HTTPHandler(concurrent_limit=1)
response: Final[httpx.Response] = safe_get(client, image_url)
response.raise_for_status() # Raise an exception for HTTP errors
# Check the response's content type to ensure it is an image
content_type: Final = response.headers.get("content-type")
if not content_type or "image" not in content_type:
raise ValueError(f"URL does not point to a valid image (content-type: {content_type})")
# Load the image from the response content
return Image.open(BytesIO(response.content))
except Exception as e:
raise e
def _gemini_vision_convert_messages(messages: list):
"""
Converts given messages for GPT-4 Vision to Gemini format.
Args:
messages (list): The messages to convert. Each message can be a dictionary with a "content" key. The content can be a string or a list of elements. If it is a string, it will be concatenated to the prompt. If it is a list, each element will be processed based on its type:
- If the element is a dictionary with a "type" key equal to "text", its "text" value will be concatenated to the prompt.
- If the element is a dictionary with a "type" key equal to "image_url", its "image_url" value will be added to the list of images.
Returns:View on GitHub (pinned to 6c2dcb801b)
Solutions
- curl -I <url> and confirm content-type starts with image/; if not, fix the URL or its auth
- Regenerate expired presigned URLs before each request
- Download the image yourself (with proper auth headers) and pass it as a base64 data URI instead
Example fix
# before
image_url = "https://my-bucket.s3.amazonaws.com/cat.png?X-Amz-Signature=..." # expired -> XML error page
msg = {"type": "image_url", "image_url": {"url": image_url}}
# after
import boto3, base64
s3 = boto3.client("s3")
obj = s3.get_object(Bucket="my-bucket", Key="cat.png")
b64 = base64.b64encode(obj["Body"].read()).decode()
msg = {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}} Defensive patterns
Strategy: validation
Validate before calling
import httpx
def url_is_image(url: str) -> bool:
r = httpx.head(url, follow_redirects=True, timeout=10)
if r.status_code == 405:
r = httpx.get(url, headers={"Range": "bytes=0-0"}, follow_redirects=True, timeout=10)
return r.headers.get("content-type", "").startswith("image/") Try / catch
try:
litellm.completion(model=model, messages=messages)
except Exception as e:
if "URL does not point to a valid image" in str(e):
data = httpx.get(local_fetch(url), timeout=30).content # fetch with auth locally
messages = inject_data_uri(messages, "data:image/png;base64," + base64.b64encode(data).decode())
litellm.completion(model=model, messages=messages)
else:
raise Prevention
- Verify content-type with a HEAD request before passing URLs
- Regenerate presigned URLs close to request time
- Fetch auth-protected images yourself and inline base64
When it happens
Trigger: Image URLs that return text/html (login pages, S3 XML errors, 404 pages served as 200), endpoints with no content-type header, or presigned URLs that expired and redirect to an error document; reached from Gemini vision conversion of https:// URLs.
Common situations: Expired presigned S3/GCS URLs; intranet URLs behind auth proxies that return HTML; CDN URLs requiring headers your request lacks; typos in domains that resolve to parked pages.
Related errors
- Unable to determine content type from URL: {url}. Response c
- Failed to transform Braintrust response: {str(e)}
- Error apply_db_fixes: {str(e)}
- Error from qdrant checking if /collections exist {collection
- Invalid image URL: {content_image_url}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/a032222eab9d3e3c.
Report an issue: GitHub.