{"record":{"id":"ce769adfd6ac971b","repo":"BerriAI/litellm","slug":"content-must-be-a-string","errorCode":null,"errorMessage":"Content must be a string","messagePattern":"Content must be a string","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/llms/bytez/chat/transformation.py","lineNumber":446,"sourceCode":"        if isinstance(content, str):\n            new_content.append({\"type\": \"text\", \"text\": content})\n\n        elif isinstance(content, dict):\n            new_content.append(content)\n\n        elif isinstance(content, list):\n            new_content_items = []\n            for content_item in content:\n                if isinstance(content_item, str):\n                    new_content_items.append({\"type\": \"text\", \"text\": content_item})\n                elif isinstance(content_item, dict):\n                    new_content_items.append(content_item)\n                else:\n                    raise Exception(\"`content` can only contain strings or openai content dicts\")\n\n            new_content += new_content_items\n        else:\n            raise Exception(\"Content must be a string\")\n\n        new_messages.append({\"role\": role, \"content\": new_content})\n\n    return new_messages\n\n\n# TODO get this from the api instead of doing it here, will require backend work\ndef get_tokens_from_messages(messages: list[dict]):\n    total = 0\n\n    for message in messages:\n        content: list[dict] = message[\"content\"]\n\n        for content_item in content:\n            type = content_item[\"type\"]\n            if type == \"text\":\n                value: str = content_item[\"text\"]\n                words = value.split(\" \")","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/bytez/chat/transformation.py#L428-L464","documentation":"Thrown by Bytez's content normalization when a message's `content` field is not a str, dict, or list (e.g. an int, None, or bytes). The helper only understands string, single-dict, and list-of-parts shapes; anything else is rejected client-side before an HTTP request is made.","triggerScenarios":"Sending `messages=[{\"role\": \"user\", \"content\": 42}]` or `content: None` to a Bytez model. Also happens when tool/result messages are constructed with non-string payloads (e.g. raw JSON bytes or a number) that other providers tolerate.","commonSituations":"Interpolating a computed value into content without `str()` conversion; forwarding `None` content for assistant/tool messages; passing structured data (dict output of a function) as the whole content instead of `json.dumps(data)`.","solutions":["Wrap non-string content in `str(...)` or `json.dumps(...)` before the call.","Replace `content: None` with `content: \"\"` or omit the message.","Validate message shape client-side with a typed message builder.","Re-check the failing message index from the traceback and fix that specific entry."],"exampleFix":"// before\nmessages = [{\"role\": \"user\", \"content\": user_id}]  # int\n\n// after\nmessages = [{\"role\": \"user\", \"content\": str(user_id)}]","handlingStrategy":"validation","validationCode":"def validate_message_shape(messages):\n    for i, m in enumerate(messages):\n        c = m.get(\"content\")\n        if not isinstance(c, (str, dict, list)):\n            raise ValueError(f\"messages[{i}].content must be str/dict/list, got {type(c).__name__}\")","typeGuard":"def has_valid_content(message) -> bool:\n    return isinstance(message.get(\"content\"), (str, dict, list))","tryCatchPattern":null,"preventionTips":["Always stringify computed values: str(x) or json.dumps(x).","Use None content only where the provider documents it; Bytez does not accept it.","Type your message builders (TypedDict/dataclass) so content is always str or list[parts]."],"tags":["bytez","request-validation","message-format"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}