{"record":{"id":"864ca37969ffe986","repo":"VectifyAI/PageIndex","slug":"messages-must-contain-a-user-or-assistant-message","errorCode":null,"errorMessage":"messages must contain a user or assistant message.","messagePattern":"messages must contain a user or assistant message\\.","errorType":"exception","errorClass":"PageIndexAPIError","httpStatus":null,"severity":"error","filePath":"pageindex/local_chat.py","lineNumber":87,"sourceCode":"                \"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)\n\n\n_SENTINEL = object()\n\n\ndef _stream_sync(agen_factory) -> Iterator[Any]:\n    \"\"\"Drive an async generator from a background thread; yield synchronously.\n\n    Closing the iterator cancels the run between items: the pump stops, and\n    the async generator's cleanup cancels the underlying agent task, so no\n    further model turns or tool executions start. An in-flight backend","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/VectifyAI/PageIndex/blob/afb5e119766630af6014b04fe8b53357527bc05e/pageindex/local_chat.py#L69-L105","documentation":"After validation, chat_completions requires at least one user or assistant message in the list. A messages array consisting only of system/developer messages (even if non-empty) fails, since there is no conversation to drive.","triggerScenarios":"messages=[{\"role\":\"system\",\"content\":\"...\"}] only; or all user/assistant messages were filtered out by earlier processing.","commonSituations":"Building system prompt first and forgetting to append the user question, upstream filters stripping user turns (moderation, truncation), replaying config-only transcripts.","solutions":["Always append the user's question: messages.append({\"role\":\"user\",\"content\":query})","Guard: if not any(m['role'] in ('user','assistant') for m in messages): abort early","Check upstream filtering logic isn't dropping user turns"],"exampleFix":"# before\nclient.chat_completions(model=m, messages=[system_msg])\n\n# after\nclient.chat_completions(model=m, messages=[system_msg, {\"role\":\"user\",\"content\":question}])","handlingStrategy":"validation","validationCode":"if not any(m.get('role') in ('user','assistant') for m in messages):\n    messages = messages + [{'role':'user','content':query}]","typeGuard":"def has_conversation_turn(msgs) -> bool:\n    return any(isinstance(m, dict) and m.get('role') in ('user','assistant') for m in msgs)","tryCatchPattern":"null","preventionTips":["Always append the user question after the system prompt","Guard against filters stripping user turns","Test prompt-building code with empty inputs"],"tags":["pageindex","chat","messages","validation"],"backgroundTag":"empty-messages-array","analyzedSha":"afb5e119766630af6014b04fe8b53357527bc05e","analyzedAt":"2026-08-27T11:20:48.519Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}