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
- PIL image: `buf = io.BytesIO(); pil_img.save(buf, format="PNG"); image = buf.getvalue()`.
- numpy/cv2 frame: `image = cv2.imencode(".png", frame)[1].tobytes()`.
- pathlib.Path: `image = path.read_bytes()` (the Path object itself is rejected).
- 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
- Convert PIL/numpy/Path objects to bytes at your application boundary, not deep in call sites.
- Never forward an unvalidated optional value - branch on None explicitly.
- Add a unit test asserting your pipeline passes only bytes/file-like/URL into litellm.
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
- Max recursion depth reached while reading image bytes for…
- Unsupported image input: plain string values that are not…
- Error parsing BFL response
- No image URL in BFL result
- Unsupported image type for Vertex AI Imagen image edit. Got…
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 imageView on GitHub (pinned to 77b7c6c40c)