{"record":{"id":"db7e7d449799692b","repo":"BerriAI/litellm","slug":"max-recursion-depth-max-depth-reached-while-read-db7e7d","errorCode":null,"errorMessage":"Max recursion depth {max_depth} reached while reading image bytes for Vertex AI Imagen image edit.","messagePattern":"Max recursion depth (.+?) reached while reading image bytes for Vertex AI Imagen image edit\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/vertex_ai/image_edit/vertex_imagen_transformation.py","lineNumber":290,"sourceCode":"            mask_bytes: Final = self._read_all_bytes(mask_image)\n            mask_base64: Final = base64.b64encode(mask_bytes).decode(\"utf-8\")\n\n            mask_reference: Final = {\n                \"referenceType\": \"REFERENCE_TYPE_MASK\",\n                \"referenceId\": len(reference_images) + 1,\n                \"referenceImage\": {\"bytesBase64Encoded\": mask_base64},\n                \"maskImageConfig\": {\n                    \"maskMode\": \"MASK_MODE_USER_PROVIDED\",\n                    \"dilation\": 0.03,  # Default dilation value (not configurable via OpenAI API)\n                },\n            }\n            reference_images.append(mask_reference)\n\n        return reference_images\n\n    def _read_all_bytes(self, image: Any, depth: int = 0, max_depth: int = DEFAULT_MAX_RECURSE_DEPTH) -> bytes:\n        if depth > max_depth:\n            raise ValueError(\n                f\"Max recursion depth {max_depth} reached while reading image bytes for Vertex AI Imagen image edit.\"\n            )\n\n        if isinstance(image, (list, tuple)):\n            for item in image:\n                if item is not None:\n                    return self._read_all_bytes(item, depth=depth + 1, max_depth=max_depth)\n            raise ValueError(\"Unsupported image type for Vertex AI Imagen image edit.\")\n\n        if isinstance(image, dict):\n            for key in (\"data\", \"bytes\", \"content\"):\n                if key in image and image[key] is not None:\n                    value = image[key]\n                    if isinstance(value, str):\n                        try:\n                            return base64.b64decode(value)\n                        except Exception:\n                            continue","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/vertex_ai/image_edit/vertex_imagen_transformation.py#L272-L308","documentation":"Imagen edit's _read_all_bytes recursively unwraps structured image input — lists/tuples take the first non-None item, dicts are probed for 'data'/'bytes'/'content' (base64-decoded) then 'path'. A depth guard caps this recursion at DEFAULT_MAX_RECURSE_DEPTH (100 by default, overridable via the DEFAULT_MAX_RECURSE_DEPTH env var) to avoid pathological structures. Only inputs nested deeper than that cap raise this ValueError.","triggerScenarios":"image=[[[[ ... ]]]] with more than 100 nesting levels; a dict chain like {'data': {'bytes': {'content': ...}}} deeper than 100; usually generated by buggy glue code that re-wraps images in a loop (e.g. image = [image] applied repeatedly).","commonSituations":"Loop bugs that wrap payloads one more layer per iteration; data pipelines that forward arbitrary user JSON as the image field; test fixtures generated recursively.","solutions":["Flatten the input before the call — reduce it to bytes, BytesIO, or a shallow {'data': '<base64>'} dict","Find and fix the wrapping loop that adds a layer per iteration (the real bug is upstream)","As a last resort for legitimately deep structures, raise the cap via env var DEFAULT_MAX_RECURSE_DEPTH=200"],"exampleFix":"# before: loop adds a layer per retry\nfor attempt in range(retries):\n    payload = [payload]  # grows nesting each pass\nresp = litellm.image_edit(model='vertex_ai/imagen-...', prompt=p, image=payload)\n\n# after: send a flat payload\npayload = unwrap_to_bytes(payload)  # collapse to bytes once\nresp = litellm.image_edit(model='vertex_ai/imagen-...', prompt=p, image=payload)","handlingStrategy":"validation","validationCode":"def nesting_depth(v, d=0):\n    if isinstance(v, (list, tuple)) and v:\n        return nesting_depth(v[0], d + 1)\n    return d\n\nassert nesting_depth(image) < 100, 'image payload suspiciously nested; flatten to bytes'","typeGuard":null,"tryCatchPattern":"try:\n    resp = litellm.image_edit(model='vertex_ai/imagen-...', prompt=p, image=image)\nexcept ValueError as e:\n    if 'Max recursion depth' in str(e):\n        raise ValueError('image payload nesting bug — flatten to bytes before calling') from e\n    raise","preventionTips":["Normalize image payloads to bytes at the system boundary; never forward arbitrary user JSON","Audit loops that wrap payloads (payload = [payload]) per retry or per pipeline stage","Add a depth assertion in preprocessing so bad structures fail with your own error message"],"tags":["vertex-ai","imagen","image-edit","recursion","input-validation"],"backgroundTag":"max-recursion-depth-exceeded","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}