BerriAI/litellm · error · ValueError

Unsupported image type

Error message

Unsupported image type: {type(image)}. Expected bytes, str (URL or file path), or file-like object.

What it means

The final else-branch of `_read_image_bytes` in the BFL image-edit transformer: the `image` argument was neither `bytes`, a `list`, a `str`, nor an object exposing `.read()` (file-like). LiteLLM therefore has no way to extract raw bytes and raises this ValueError. Note the message mentions 'file path' but the str branch above only actually supports http(s) URLs.

Solutions

  1. PIL image: `buf = io.BytesIO(); pil_img.save(buf, format="PNG"); image = buf.getvalue()`.
  2. numpy/cv2 frame: `image = cv2.imencode(".png", frame)[1].tobytes()`.
  3. pathlib.Path: `image = path.read_bytes()` (the Path object itself is rejected).
  4. If the value can legitimately be absent, branch on it (`if image is not None`) instead of forwarding None.

Example fix

# before
from PIL import Image
img = Image.open("cat.png")
resp = litellm.image_edit(model="flux-kontext-pro", image=img, prompt="...")  # raises

# after
import io
from PIL import Image
buf = io.BytesIO()
Image.open("cat.png").save(buf, format="PNG")
resp = litellm.image_edit(model="flux-kontext-pro", image=buf.getvalue(), prompt="...")
Defensive patterns

Strategy: type-guard

Validate before calling

import io

def coerce_to_bytes(image) -> bytes:
    if isinstance(image, (bytes, bytearray)):
        return bytes(image)
    if hasattr(image, "read"):
        return image.read()
    if image.__class__.__name__ == "Image":            # PIL
        buf = io.BytesIO(); image.save(buf, format="PNG"); return buf.getvalue()
    if hasattr(image, "tobytes"):                       # numpy
        return image.tobytes()
    if hasattr(image, "read_bytes"):                    # pathlib.Path
        return image.read_bytes()
    raise TypeError(f"cannot convert {type(image)} to image bytes")

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

Type guard

from typing import Any

def is_bfl_supported_image(x: Any) -> bool:
    if isinstance(x, (bytes, bytearray, list)):
        return True
    if isinstance(x, str):
        return x.startswith(("http://", "https://"))
    return hasattr(x, "read") and callable(x.read)

Prevention

When it happens

Trigger: Passing a `pathlib.Path` object (has no `.read()` attr - `Path.read_bytes` exists but `read` does not); a PIL `Image.Image`; a `numpy.ndarray`; an `io.StringIO`; a generator; `None` accidentally forwarded from an optional field; a boto3 StreamingBody (no `.read` until iterated, though most have read - generators and Path are the classic hits).

Common situations: Pipelines that load images with Pillow and pass the PIL object straight through; numpy-based preprocessing (cv2/np arrays); Windows/Linux portability code using pathlib; an upstream optional param defaulting to None being forwarded unconditionally.

Related errors


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

Appendix: source

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

                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(
        self,
        model: str,
        prompt: str | None,
        image: FileTypes | None,
        image_edit_optional_request_params: dict,
        litellm_params: GenericLiteLLMParams,
        headers: dict,
    ) -> tuple[dict, RequestFiles]:
        """
        Transform OpenAI-style request to Black Forest Labs request format.

        BFL uses JSON body with base64-encoded images, not multipart/form-data.
        """
        # Read and encode image

View on GitHub (pinned to 77b7c6c40c)