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
- For a local file, open it and pass the file object or its bytes: `image=open(path, "rb")` or `image=Path(path).read_bytes()`.
- For base64 data you already hold as a string, decode it first: `image=base64.b64decode(data)`.
- For an image behind http(s), pass the full URL including the scheme (`https://...`) - a scheme-less host string will be rejected.
- 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
- Standardize on bytes or file objects for local images in your codebase.
- Decode base64 strings yourself before passing them; this provider will not.
- Watch for scheme-less URLs ('cdn.example.com/img.png') - they fail the check.
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
- Max recursion depth reached while reading image bytes for…
- Unsupported image type
- Error parsing BFL response
- No image URL in BFL result
- BFL error
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)