BerriAI/litellm · error · ValueError

Max recursion depth {max_depth} reached while reading image

Error message

Max recursion depth {max_depth} reached while reading image bytes for Black Forest Labs image edit.

What it means

_read_image_bytes normalizes diverse image inputs (bytes, list, URL string, file-like object) into raw bytes, recursing one level per list wrapper. A depth counter guards against pathologically nested lists; exceeding DEFAULT_MAX_RECURSE_DEPTH raises ValueError. In practice this almost always means an empty or unexpectedly nested structure (e.g. a list of lists with no bytes at the leaf) rather than genuine 10+ level nesting.

Source

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

    ) -> str:
        """
        Get the complete URL for the Black Forest Labs API request.
        """
        base_url: str = api_base or get_secret_str("BFL_API_BASE") or DEFAULT_API_BASE
        base_url = base_url.rstrip("/")

        endpoint: Final = self._get_model_endpoint(model)
        return f"{base_url}{endpoint}"

    def _read_image_bytes(
        self,
        image: Any,
        depth: int = 0,
        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"):

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Pass the image directly: raw bytes, a single URL string, or one file-like object.
  2. If you must use a list, use exactly one level: image=[b"..."].
  3. Inspect the value right before the call: print(type(image), len(image)) down the nesting to find where extra wrappers come from.
  4. Fix the upstream code that keeps collecting the image into another list on every layer.

Example fix

# before
img = load_bytes()
litellm.image_edit(model=..., image=[[[img]]], prompt="...")

# after
img = load_bytes()
litellm.image_edit(model=..., image=img, prompt="...")
Defensive patterns

Strategy: validation

Validate before calling

def flat_image(x, depth=0):
    while isinstance(x, list):
        if not x: raise ValueError("empty image list")
        x = x[0]; depth += 1
        if depth > 5: raise ValueError("image wrapped in too many lists")
    return x

image = flat_image(image)

Type guard

from typing import Any

def is_recursion_safe_image(x: Any, depth: int = 0) -> bool:
    if isinstance(x, (bytes,)) or (isinstance(x, str) and x.startswith(("http://","https://"))) or hasattr(x, "read"):
        return True
    if isinstance(x, list) and x:
        return depth < 5 and is_recursion_safe_image(x[0], depth + 1)
    return False

Try / catch

null

Prevention

When it happens

Trigger: Passing image as deeply nested lists, e.g. [[[...[b"..."]...]]], or a structure whose leaf elements are never bytes/str/file-like so each recursion only descends (an empty list would IndexError first, but nested single-element lists chain to the depth cap).

Common situations: Wrapping the image argument multiple times by mistake (e.g. image=[image_list] where image_list was already a list); passing a parsed JSON/AI-message content array instead of the media payload; programmatic construction of the image arg that accumulates wrappers across layers.

Related errors


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