{"record":{"id":"ff46e9fffed9668e","repo":"VectifyAI/PageIndex","slug":"chat-completions-content-must-be-a-string-for-str","errorCode":null,"errorMessage":"chat_completions content must be a string; for structured items use responses() or messages().","messagePattern":"chat_completions content must be a string; for structured items use responses\\(\\) or messages\\(\\)\\.","errorType":"exception","errorClass":"PageIndexAPIError","httpStatus":null,"severity":"error","filePath":"pageindex/local_chat.py","lineNumber":76,"sourceCode":"def _split_chat_messages(messages) -> \"tuple[list[str], list[dict]]\":\n    \"\"\"Validate the chat_completions surface's messages: system/developer\n    content joins the managed instructions; user/assistant history passes\n    through. Tool-history round-trips belong to responses()/messages().\"\"\"\n    if not isinstance(messages, list) or not messages:\n        raise PageIndexAPIError(\"messages must be a non-empty list.\")\n    system_texts: list[str] = []\n    history: list[dict] = []\n    for message in messages:\n        if not isinstance(message, dict) or \"role\" not in message:\n            raise PageIndexAPIError(\n                \"Each message must be a dict with 'role' and 'content'.\")\n        role = message[\"role\"]\n        if role in (\"system\", \"developer\"):\n            system_texts.append(_system_text(message.get(\"content\")))\n        elif role in (\"user\", \"assistant\"):\n            content = message.get(\"content\")\n            if not isinstance(content, str):\n                raise PageIndexAPIError(\n                    \"chat_completions content must be a string; for \"\n                    \"structured items use responses() or messages().\"\n                )\n            history.append({\"role\": role, \"content\": content})\n        else:\n            raise PageIndexAPIError(\n                f\"Unsupported role for chat_completions: {role!r}. Tool \"\n                \"history round-trips belong to responses() or messages().\"\n            )\n    if not history:\n        raise PageIndexAPIError(\"messages must contain a user or assistant \"\n                                \"message.\")\n    return system_texts, history\n\n\ndef _run_sync(coro):\n    from .utils import run_off_loop\n    return run_off_loop(asyncio.run, coro)","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/VectifyAI/PageIndex/blob/afb5e119766630af6014b04fe8b53357527bc05e/pageindex/local_chat.py#L58-L94","documentation":"On the chat_completions surface, user/assistant message content must be a plain string. Structured content (lists of parts, tool calls) is intentionally rejected — those round-trips belong to the responses() or messages() surfaces which understand item dicts.","triggerScenarios":"Passing {\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"hi\"}]} or assistant messages containing tool_calls to chat_completions.","commonSituations":"Replaying captured OpenAI request bodies that use content-part arrays, mixing surfaces when porting between chat_completions and responses, feeding back assistant output with structured parts.","solutions":["Flatten content parts to a string: \"\\n\".join(p[\"text\"] for p in content)","Use client.responses(...) or client.messages(...) when history contains structured items or tool calls","Strip tool_call entries from history before chat_completions"],"exampleFix":"# before\nmsgs=[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"hi\"}]}]\nclient.chat_completions(model=m, messages=msgs)\n\n# after\nmsgs=[{\"role\":\"user\",\"content\":\"hi\"}]\nclient.chat_completions(model=m, messages=msgs)","handlingStrategy":"validation","validationCode":"def flatten(content):\n    if isinstance(content, str): return content\n    if isinstance(content, list): return \"\\n\".join(p.get('text','') for p in content if isinstance(p, dict))\n    return content\nmessages = [{**m, 'content': flatten(m['content'])} for m in messages]","typeGuard":"def has_string_content(m) -> bool:\n    c = m.get('content')\n    return c is None or isinstance(c, str)","tryCatchPattern":"null","preventionTips":["Flatten content-part arrays to strings for chat_completions","Route tool-call history to responses()/messages()","Strip assistant tool_calls before replaying history"],"tags":["pageindex","chat","content-parts","validation"],"backgroundTag":"message-content-format-invalid","analyzedSha":"afb5e119766630af6014b04fe8b53357527bc05e","analyzedAt":"2026-08-27T11:20:48.519Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}