BerriAI/litellm · error · ValueError

Unsupported image input: plain string values that are not…

Error message

Unsupported image input: plain string values that are not URLs are not accepted. Provide image bytes or a file-like object.

What it means

`_read_image_bytes` in the BFL image-edit transformer only accepts a Python `str` if it starts with `http://` or `https://` (it then downloads it with a 60s GET). Any other string - a local file path, a bare base64 payload, a `data:`/`gs://`/`file://` URI - hits this ValueError. The library deliberately refuses to guess, so it does not read local paths or decode base64 for you.

Solutions

  1. For a local file, open it and pass the file object or its bytes: `image=open(path, "rb")` or `image=Path(path).read_bytes()`.
  2. For base64 data you already hold as a string, decode it first: `image=base64.b64decode(data)`.
  3. For an image behind http(s), pass the full URL including the scheme (`https://...`) - a scheme-less host string will be rejected.
  4. For cloud-storage objects (s3/gs), download to bytes first with your storage SDK, then pass the bytes.

Example fix

# before
resp = litellm.image_edit(model="flux-kontext-pro", image="/tmp/cat.png", prompt="add a hat")  # raises

# after
from pathlib import Path
resp = litellm.image_edit(model="flux-kontext-pro", image=Path("/tmp/cat.png").read_bytes(), prompt="add a hat")
Defensive patterns

Strategy: type-guard

Validate before calling

from pathlib import Path
import base64, re

def to_bfl_image(image):
    if isinstance(image, str):
        if image.startswith(("http://", "https://")):
            return image
        if re.match(r"^[A-Za-z0-9+/]+={0,2}$", image) and len(image) > 64:
            return base64.b64decode(image)          # bare base64 -> bytes
        return Path(image).read_bytes()              # assume local path
    return image

image = to_bfl_image(image)  # before litellm.image_edit(...)

Type guard

def is_bfl_url_string(x) -> bool:
    return isinstance(x, str) and x.startswith(("http://", "https://"))

Prevention

When it happens

Trigger: Passing `image="/home/user/cat.png"` or `image="cat.png"`; passing a base64 string `image="iVBORw0KGgo..."`; passing a data URI `image="data:image/png;base64,..."`; passing an `s3://`/`gs://`/`ftp://` URL. All are strings that do not start with http(s), so the else-branch raises.

Common situations: Porting code from OpenAI's images/edits API (which takes a file path or file object) or from other LiteLLM image providers (e.g. Gemini/GPT-image which accept base64 strings); passing a relative path built with os.path.join; assuming the 'str (URL or file path)' wording in the sibling 'Unsupported image type' error means paths are supported here - they are not in this code path.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/8cb679dbfbbce6d7. Report an issue: GitHub.

Appendix: source

Thrown at litellm/llms/black_forest_labs/image_edit/transformation.py:212

        max_depth: int = DEFAULT_MAX_RECURSE_DEPTH,
    ) -> bytes:
        """Read image bytes from various input types."""
        if depth > max_depth:
            raise ValueError(
                f"Max recursion depth {max_depth} reached while reading image bytes for Black Forest Labs image edit."
            )
        if isinstance(image, bytes):
            return image
        elif isinstance(image, list):
            # If it's a list, take the first image
            return self._read_image_bytes(image[0], depth=depth + 1, max_depth=max_depth)
        elif isinstance(image, str):
            if image.startswith(("http://", "https://")):
                response: Final = safe_get(litellm.module_level_client, image, timeout=60.0)
                response.raise_for_status()
                return response.content
            else:
                raise ValueError(
                    "Unsupported image input: plain string values that are not URLs are not accepted. "
                    "Provide image bytes or a file-like object."
                )
        elif hasattr(image, "read"):
            # File-like object
            pos: Final = getattr(image, "tell", lambda: 0)()
            if hasattr(image, "seek"):
                image.seek(0)
            data: Final = image.read()
            if hasattr(image, "seek"):
                image.seek(pos)
            return data
        else:
            raise ValueError(
                f"Unsupported image type: {type(image)}. Expected bytes, str (URL or file path), or file-like object."
            )

    def transform_image_edit_request(

View on GitHub (pinned to 77b7c6c40c)