{"record":{"id":"8cb679dbfbbce6d7","repo":"BerriAI/litellm","slug":"unsupported-image-input-plain-string-values-that","errorCode":null,"errorMessage":"Unsupported image input: plain string values that are not URLs are not accepted. Provide image bytes or a file-like object.","messagePattern":"Unsupported image input: plain string values that are not URLs are not accepted\\. Provide image bytes or a file-like object\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/black_forest_labs/image_edit/transformation.py","lineNumber":212,"sourceCode":"        max_depth: int = DEFAULT_MAX_RECURSE_DEPTH,\n    ) -> bytes:\n        \"\"\"Read image bytes from various input types.\"\"\"\n        if depth > max_depth:\n            raise ValueError(\n                f\"Max recursion depth {max_depth} reached while reading image bytes for Black Forest Labs image edit.\"\n            )\n        if isinstance(image, bytes):\n            return image\n        elif isinstance(image, list):\n            # If it's a list, take the first image\n            return self._read_image_bytes(image[0], depth=depth + 1, max_depth=max_depth)\n        elif isinstance(image, str):\n            if image.startswith((\"http://\", \"https://\")):\n                response: Final = safe_get(litellm.module_level_client, image, timeout=60.0)\n                response.raise_for_status()\n                return response.content\n            else:\n                raise ValueError(\n                    \"Unsupported image input: plain string values that are not URLs are not accepted. \"\n                    \"Provide image bytes or a file-like object.\"\n                )\n        elif hasattr(image, \"read\"):\n            # File-like object\n            pos: Final = getattr(image, \"tell\", lambda: 0)()\n            if hasattr(image, \"seek\"):\n                image.seek(0)\n            data: Final = image.read()\n            if hasattr(image, \"seek\"):\n                image.seek(pos)\n            return data\n        else:\n            raise ValueError(\n                f\"Unsupported image type: {type(image)}. Expected bytes, str (URL or file path), or file-like object.\"\n            )\n\n    def transform_image_edit_request(","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/black_forest_labs/image_edit/transformation.py#L194-L230","documentation":"`_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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nresp = litellm.image_edit(model=\"flux-kontext-pro\", image=\"/tmp/cat.png\", prompt=\"add a hat\")  # raises\n\n# after\nfrom pathlib import Path\nresp = litellm.image_edit(model=\"flux-kontext-pro\", image=Path(\"/tmp/cat.png\").read_bytes(), prompt=\"add a hat\")","handlingStrategy":"type-guard","validationCode":"from pathlib import Path\nimport base64, re\n\ndef to_bfl_image(image):\n    if isinstance(image, str):\n        if image.startswith((\"http://\", \"https://\")):\n            return image\n        if re.match(r\"^[A-Za-z0-9+/]+={0,2}$\", image) and len(image) > 64:\n            return base64.b64decode(image)          # bare base64 -> bytes\n        return Path(image).read_bytes()              # assume local path\n    return image\n\nimage = to_bfl_image(image)  # before litellm.image_edit(...)","typeGuard":"def is_bfl_url_string(x) -> bool:\n    return isinstance(x, str) and x.startswith((\"http://\", \"https://\"))","tryCatchPattern":null,"preventionTips":["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."],"tags":["litellm","black-forest-labs","flux","image-edit","input-validation","base64","file-path"],"backgroundTag":"unsupported-input-type","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}