{"record":{"id":"4ef83dfddaccdff7","repo":"VectifyAI/PageIndex","slug":"each-message-must-be-a-dict-with-role-and-conte","errorCode":null,"errorMessage":"Each message must be a dict with 'role' and 'content'.","messagePattern":"Each message must be a dict with 'role' and 'content'\\.","errorType":"exception","errorClass":"PageIndexAPIError","httpStatus":null,"severity":"error","filePath":"pageindex/local_chat.py","lineNumber":68,"sourceCode":"                 if isinstance(part, dict) and isinstance(part.get(\"text\"), str)]\n        if texts:\n            return \"\\n\".join(texts)\n    raise PageIndexAPIError(\n        \"system message content must be a string or a list of text parts.\"\n    )\n\n\ndef _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:","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/VectifyAI/PageIndex/blob/afb5e119766630af6014b04fe8b53357527bc05e/pageindex/local_chat.py#L50-L86","documentation":"Each element of the messages list must be a dict containing at least a 'role' key. Strings, tuples, or dicts without 'role' are rejected — this mirror of the OpenAI chat schema is enforced before the model runs.","triggerScenarios":"messages=[\"hello\"], messages=[(\"user\",\"hi\")], or messages=[{\"content\":\"hi\"}] (missing role).","commonSituations":"Hand-building message arrays, JSON payloads with typos ('Role'), converting from Anthropic-style tuples, LLM-generated request bodies with schema drift.","solutions":["Use {\"role\": ..., \"content\": ...} dicts for every message","Validate each item: all(isinstance(m, dict) and 'role' in m for m in messages)","If converting from another SDK's format, write an explicit adapter"],"exampleFix":"# before\nmessages=[{\"content\": \"hi\"}]\n\n# after\nmessages=[{\"role\": \"user\", \"content\": \"hi\"}]","handlingStrategy":"validation","validationCode":"for m in messages:\n    if not isinstance(m, dict) or 'role' not in m:\n        raise ValueError(f'bad message: {m!r}')","typeGuard":"def is_valid_message(m) -> bool:\n    return isinstance(m, dict) and 'role' in m and 'content' in m","tryCatchPattern":"null","preventionTips":["Always build messages as {role, content} dicts","Schema-validate external message payloads (pydantic/jsonschema)","Watch for role-key typos in JSON"],"tags":["pageindex","chat","messages","role","validation"],"backgroundTag":"message-schema-invalid","analyzedSha":"afb5e119766630af6014b04fe8b53357527bc05e","analyzedAt":"2026-08-27T11:20:48.519Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}