{"record":{"id":"bb10f59ed862b7bc","repo":"BerriAI/litellm","slug":"message-content-must-be-a-string-or-list-of-cont","errorCode":null,"errorMessage":"Message `content` must be a string or list of content parts","messagePattern":"Message `content` must be a string or list of content parts","errorType":"validation","errorClass":"OCIError","httpStatus":400,"severity":"error","filePath":"litellm/llms/oci/chat/generic.py","lineNumber":187,"sourceCode":"def adapt_messages_to_generic_oci_standard(\n    messages: list[AllMessageValues],\n) -> list[OCIMessage]:\n    \"\"\"Convert an OpenAI-format message array to OCI GENERIC format.\"\"\"\n    new_messages: Final = []\n    for message in messages:\n        role = message[\"role\"]\n        content = message.get(\"content\")\n        tool_calls = message.get(\"tool_calls\")\n        tool_call_id = message.get(\"tool_call_id\")\n\n        if role == \"assistant\" and tool_calls is not None:\n            if not isinstance(tool_calls, list):\n                raise OCIError(status_code=400, message=\"Message `tool_calls` must be a list\")\n            new_messages.append(adapt_messages_to_generic_oci_standard_tool_call(role, tool_calls))\n\n        elif role in [\"system\", \"user\", \"assistant\"] and content is not None:\n            if not isinstance(content, (str, list)):\n                raise OCIError(\n                    status_code=400,\n                    message=\"Message `content` must be a string or list of content parts\",\n                )\n            new_messages.append(adapt_messages_to_generic_oci_standard_content_message(role, content))\n\n        elif role == \"tool\":\n            if not isinstance(tool_call_id, str):\n                raise OCIError(\n                    status_code=400,\n                    message=\"Tool result message must have a string `tool_call_id`\",\n                )\n            if not isinstance(content, str):\n                raise OCIError(\n                    status_code=400,\n                    message=\"Tool result message `content` must be a string\",\n                )\n            new_messages.append(adapt_messages_to_generic_oci_standard_tool_response(role, tool_call_id, content))\n","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/oci/chat/generic.py#L169-L205","documentation":"For system/user/assistant messages that carry content, the OCI GENERIC adapter accepts content as either a plain string or a list of content-part dicts. Any other type (dict, int, None-handled-elsewhere objects, bytes) raises OCIError(400) 'Message `content` must be a string or list of content parts' client-side.","triggerScenarios":"Sending {'role':'user','content': {'text':'hi'}} (dict instead of list of parts), content as a number, or content as raw bytes to an oci/ GENERIC model.","commonSituations":"Data-driven prompts where content is sometimes a single object; YAML/JSON configs parsed into dicts and passed through unmodified; content accidentally left as a template object or lazy proxy.","solutions":["Use 'content': 'plain text' or 'content': [{'type':'text','text':'...'}, ...].","Coerce unknown content: str() scalars, or wrap dicts into part lists, before calling litellm.","Add a pre-flight type check on every message's content field."],"exampleFix":"# before\n{'role':'user','content': {'text':'hello'}}\n\n# after\n{'role':'user','content': 'hello'}\n# or\n{'role':'user','content': [{'type':'text','text':'hello'}]}","handlingStrategy":"type-guard","validationCode":"def normalize_message_content(msg):\n    content = msg.get('content')\n    if isinstance(content, dict):\n        msg['content'] = [content] if set(content) & {'type', 'text', 'image_url'} else str(content)\n    elif not isinstance(content, (str, list, type(None))):\n        msg['content'] = str(content)\n    return msg","typeGuard":"def message_content_is_valid(msg: object) -> bool:\n    if not isinstance(msg, dict):\n        return False\n    c = msg.get('content')\n    return c is None or isinstance(c, (str, list))","tryCatchPattern":null,"preventionTips":["Validate the whole messages array with a shared checker before every provider call.","Keep prompt data as strings end-to-end; convert structured data to text at build time."],"tags":["oci","input-validation","messages","type-mismatch"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}