BerriAI/litellm · error · ValueError

Invalid image URL: {content_image_url}

Error message

Invalid image URL: {content_image_url}

What it means

_convert_image_url raises when an image content part yields no usable URL: the part is a dict whose 'url' key is missing or None (a bare string is accepted as-is). This happens while converting chat content blocks to Responses API input_image items.

Source

Thrown at litellm/completion_extras/litellm_responses_transformation/transformation.py:832

        self, content: "ChatCompletionImageObject", role: str
    ) -> "ResponseInputImageParam":
        from openai.types.responses import ResponseInputImageParam

        content_image_url: Final = content.get("image_url")
        actual_image_url: str | None = None
        detail: Literal["low", "high", "auto"] | None = None

        if isinstance(content_image_url, str):
            actual_image_url = content_image_url
        elif isinstance(content_image_url, dict):
            actual_image_url = content_image_url.get("url")
            detail = cast(
                Literal["low", "high", "auto"] | None,
                content_image_url.get("detail"),
            )

        if actual_image_url is None:
            raise ValueError(f"Invalid image URL: {content_image_url}")

        image_param: Final = ResponseInputImageParam(image_url=actual_image_url, detail="auto", type="input_image")

        if detail:
            image_param["detail"] = detail

        return image_param

    def _convert_content_to_responses_format(
        self,
        content: str
        | list[object]
        | Iterable[
            Union["OpenAIMessageContentListBlock", "ChatCompletionThinkingBlock", "ChatCompletionRedactedThinkingBlock"]
        ]
        | None,
        role: str,
    ) -> list[dict[str, object]]:

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Ensure every image part has image_url.url set to an http(s) URL or a data: URL
  2. Validate dynamic image parts before sending: skip or fail fast when the URL source returns None
  3. Check for key typos ('src', 'imageUrl') when mapping from your own attachment model

Example fix

# before
content = [
  {"type": "text", "text": "what is this?"},
  {"type": "image_url", "image_url": {"detail": "high"}},  # no url
]

# after
content = [
  {"type": "text", "text": "what is this?"},
  {"type": "image_url", "image_url": {"url": image_src, "detail": "high"}},
]
Defensive patterns

Strategy: validation

Validate before calling

def image_parts_valid(content) -> bool:
    if isinstance(content, str):
        return True
    for part in content or []:
        if isinstance(part, dict) and part.get("type") == "image_url":
            url = (part.get("image_url") or {}).get("url")
            if not url:
                return False
    return True

Type guard

def has_valid_image_url(part: dict) -> bool:
    iu = part.get("image_url")
    return (isinstance(iu, str) and iu) or (isinstance(iu, dict) and isinstance(iu.get("url"), str) and bool(iu["url"]))

Prevention

When it happens

Trigger: Sending content: [{'type': 'image_url', 'image_url': {}}] or {'image_url': {'detail': 'high'}} with no url; None url; base64 payloads built incorrectly so the url key is dropped.

Common situations: Dynamically assembling image parts where the URL fetch failed upstream; typos like 'imageUrl' or 'src' instead of 'url'; data-URL builders returning None on error.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/42717e927be0f77d. Report an issue: GitHub.