BerriAI/litellm · error · Exception
Prop ` ` is not a string
Error message
Prop `{value_name}` is not a string What it means
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.
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.
Example fix
# before (OpenAI-style nested part -> raises Prop `url` is not a string)
messages = [{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": "https://example.com/cat.png"}},
{"type": "text", "text": "describe"},
],
}]
# after (flattened for Bytez)
messages = [{
"role": "user",
"content": [
{"type": "image_url", "url": "https://example.com/cat.png"},
{"type": "text", "text": "describe"},
],
}] Defensive patterns
Strategy: validation
Validate before calling
def flatten_parts_for_bytez(content: list) -> list:
out = []
for p in content:
t = p.get("type")
if t == "text" and p.get("text"):
out.append({"type": "text", "text": p["text"]})
elif t == "image_url":
url = p.get("url") or p.get("image_url", {}).get("url")
if url:
out.append({"type": "image_url", "url": url})
return out Type guard
def is_valid_bytez_part(part: object) -> bool:
if not isinstance(part, dict):
return False
t = part.get("type")
if t == "text":
return isinstance(part.get("text"), str) and bool(part["text"])
if t in ("image_url", "input_audio", "video_url"):
return isinstance(part.get("url"), str) and bool(part["url"])
return False Try / catch
try:
litellm.completion(model="bytez/org/model", messages=messages)
except Exception as e:
if "is not a string" in str(e):
messages = [{"role": m["role"], "content": flatten_parts_for_bytez(m["content"])} for m in messages]
litellm.completion(model="bytez/org/model", messages=messages)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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'.
Related errors
- Prop `type` is not a string
- `content` can only contain strings or openai content dicts
- Invalid image URL
- kwarg `messages` must be an array of messages that follow…
- Prop ` ` is not supported
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/86f7c42c4f125070.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/llms/bytez/chat/transformation.py:407
for content_item in content:
type: str | None = content_item.get("type")
if not type:
raise Exception("Prop `type` is not a string")
content_item_map = open_ai_to_bytez_content_item_map[type]
if not content_item_map:
raise Exception(f"Prop `{type}` is not supported")
new_type = content_item_map["type"]
value_name = content_item_map["value_name"]
value: str | None = content_item.get(value_name)
if not value:
raise Exception(f"Prop `{value_name}` is not a string")
new_content.append({"type": new_type, value_name: value})
new_messages.append({"role": role, "content": new_content})
return new_messages
# "content": "The cat ran so fast"
# becomes
# "content": [{"type": "text", "text": "The cat ran so fast"}]
def _adapt_string_only_content_to_lists(messages: list[dict]):
new_messages: Final = []
for message in messages:
role = message.get("role")
content = message.get("content")
View on GitHub (pinned to 77b7c6c40c)