{"record":{"id":"86f7c42c4f125070","repo":"BerriAI/litellm","slug":"prop-value-name-is-not-a-string","errorCode":null,"errorMessage":"Prop `{value_name}` is not a string","messagePattern":"Prop `(.+?)` is not a string","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/llms/bytez/chat/transformation.py","lineNumber":407,"sourceCode":"        for content_item in content:\n            type: str | None = content_item.get(\"type\")\n\n            if not type:\n                raise Exception(\"Prop `type` is not a string\")\n\n            content_item_map = open_ai_to_bytez_content_item_map[type]\n\n            if not content_item_map:\n                raise Exception(f\"Prop `{type}` is not supported\")\n\n            new_type = content_item_map[\"type\"]\n\n            value_name = content_item_map[\"value_name\"]\n\n            value: str | None = content_item.get(value_name)\n\n            if not value:\n                raise Exception(f\"Prop `{value_name}` is not a string\")\n\n            new_content.append({\"type\": new_type, value_name: value})\n\n        new_messages.append({\"role\": role, \"content\": new_content})\n\n    return new_messages\n\n\n# \"content\": \"The cat ran so fast\"\n# becomes\n# \"content\": [{\"type\": \"text\", \"text\": \"The cat ran so fast\"}]\ndef _adapt_string_only_content_to_lists(messages: list[dict]):\n    new_messages: Final = []\n\n    for message in messages:\n        role = message.get(\"role\")\n        content = message.get(\"content\")\n","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/bytez/chat/transformation.py#L389-L425","documentation":"Each content part's value is fetched by the mapped value_name ('text' for text parts, 'url' for image_url/input_audio/video_url); if missing or falsy this Exception names the expected prop. Crucially, Bytez expects a flattened shape: {\"type\": \"image_url\", \"url\": ...}, not OpenAI's nested {\"type\": \"image_url\", \"image_url\": {\"url\": ...}} — so standard OpenAI vision/audio parts hit this error because the top-level 'url' key doesn't exist.","triggerScenarios":"Sending canonical OpenAI multimodal parts (nested image_url object, or input_audio with data/format) to a bytez model; a text part missing its text (e.g. {\"type\": \"text\"}); empty-string url/text values.","commonSituations":"Reusing vision prompts written for OpenAI/other providers verbatim on bytez; templating that omits the url field when a variable is empty; audio parts built with 'data' instead of 'url'.","solutions":["Flatten multimodal parts for bytez: use {\"type\": \"image_url\", \"url\": \"https://...\"} instead of the nested image_url object.","Ensure every part carries its value: text parts need non-empty 'text'; image/audio/video parts need non-empty 'url'.","Guard templates so an empty variable skips the part entirely rather than sending a valueless part."],"exampleFix":"# before (OpenAI-style nested part -> raises Prop `url` is not a string)\nmessages = [{\n    \"role\": \"user\",\n    \"content\": [\n        {\"type\": \"image_url\", \"image_url\": {\"url\": \"https://example.com/cat.png\"}},\n        {\"type\": \"text\", \"text\": \"describe\"},\n    ],\n}]\n\n# after (flattened for Bytez)\nmessages = [{\n    \"role\": \"user\",\n    \"content\": [\n        {\"type\": \"image_url\", \"url\": \"https://example.com/cat.png\"},\n        {\"type\": \"text\", \"text\": \"describe\"},\n    ],\n}]","handlingStrategy":"validation","validationCode":"def flatten_parts_for_bytez(content: list) -> list:\n    out = []\n    for p in content:\n        t = p.get(\"type\")\n        if t == \"text\" and p.get(\"text\"):\n            out.append({\"type\": \"text\", \"text\": p[\"text\"]})\n        elif t == \"image_url\":\n            url = p.get(\"url\") or p.get(\"image_url\", {}).get(\"url\")\n            if url:\n                out.append({\"type\": \"image_url\", \"url\": url})\n    return out","typeGuard":"def is_valid_bytez_part(part: object) -> bool:\n    if not isinstance(part, dict):\n        return False\n    t = part.get(\"type\")\n    if t == \"text\":\n        return isinstance(part.get(\"text\"), str) and bool(part[\"text\"])\n    if t in (\"image_url\", \"input_audio\", \"video_url\"):\n        return isinstance(part.get(\"url\"), str) and bool(part[\"url\"])\n    return False","tryCatchPattern":"try:\n    litellm.completion(model=\"bytez/org/model\", messages=messages)\nexcept Exception as e:\n    if \"is not a string\" in str(e):\n        messages = [{\"role\": m[\"role\"], \"content\": flatten_parts_for_bytez(m[\"content\"])} for m in messages]\n        litellm.completion(model=\"bytez/org/model\", messages=messages)\n    else:\n        raise","preventionTips":["Remember bytez wants flattened parts: top-level 'url'/'text', not OpenAI's nested image_url/input_audio objects.","Skip parts whose value is empty instead of sending them; empty strings are treated as missing."],"tags":["bytez","messages","content-parts","multimodal","validation"],"backgroundTag":"invalid-content-part","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}