{"record":{"id":"46dff8a760f10274","repo":"BerriAI/litellm","slug":"tool-result-message-content-must-be-a-string","errorCode":null,"errorMessage":"Tool result message `content` must be a string","messagePattern":"Tool result message `content` must be a string","errorType":"validation","errorClass":"OCIError","httpStatus":400,"severity":"error","filePath":"litellm/llms/oci/chat/generic.py","lineNumber":200,"sourceCode":"                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\n    return new_messages\n\n\n# ---------------------------------------------------------------------------\n# Tool definition adaptation\n# ---------------------------------------------------------------------------\n\n\ndef adapt_tool_definition_to_oci_standard(tools: list[dict], vendor: OCIVendors) -> list[OCIToolDefinition]:\n    \"\"\"Convert OpenAI-format tool definitions to OCI GENERIC format.\n\n    Resolves ``$ref``/``$defs`` and ``anyOf`` that the OCI endpoint rejects.\n    \"\"\"","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/oci/chat/generic.py#L182-L218","documentation":"The content of a tool result message (role='tool') must be a plain string in the OCI GENERIC format. Passing a list of content parts, a dict, or None raises OCIError(400) \"Tool result message `content` must be a string\" during request construction — unlike user messages, tool replies cannot be multipart.","triggerScenarios":"Sending {'role':'tool','tool_call_id':'c1','content':[{'type':'text','text':'42'}]} or content as a dict/None after an assistant tool call, when calling an oci/ GENERIC model.","commonSituations":"Reusing a generic 'build content parts' helper for all roles including tool; structured tool outputs (JSON objects) inserted directly instead of serialized; optional tool outputs leaving None in the content slot.","solutions":["Serialize tool output: 'content': json.dumps(result) or str(result).","Special-case role='tool' in message-building helpers so it bypasses multipart formatting.","Replace None content with a placeholder string like '' or 'null'."],"exampleFix":"# before\n{'role':'tool','tool_call_id':'c1','content': [{'type':'text','text':'42'}]}\n\n# after\n{'role':'tool','tool_call_id':'c1','content': '42'}\n# structured output: 'content': json.dumps({'temp_c': 21})","handlingStrategy":"validation","validationCode":"import json\n\nmessages.append({\n    'role': 'tool',\n    'tool_call_id': tc['id'],\n    'content': result if isinstance(result, str) else json.dumps(result),\n})","typeGuard":"def tool_message_content_is_string(msg: object) -> bool:\n    return (\n        isinstance(msg, dict)\n        and msg.get('role') == 'tool'\n        and isinstance(msg.get('content'), str)\n    )","tryCatchPattern":null,"preventionTips":["In message builders, special-case role='tool' to bypass multipart content formatting.","Serialize structured tool outputs with json.dumps at the boundary; never pass None content for tool replies."],"tags":["oci","tool-calls","input-validation","type-mismatch"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}