{"record":{"id":"9de13ec26227b0bc","repo":"iflytek/astron-agent","slug":"messages-role-order-must-alternate-between-user-and","errorCode":null,"errorMessage":"messages role order must alternate between user and assistant","messagePattern":"messages role order must alternate between user and assistant","errorType":"validation","errorClass":"RequestValidationError","httpStatus":422,"severity":"error","filePath":"core/agent/api/schemas/base_inputs.py","lineNumber":67,"sourceCode":"                        }\n                    ]\n                )\n\n            if message.get(\"role\") == \"system\":\n                # System role not supported\n                raise RequestValidationError(\n                    errors=[\n                        {\n                            \"type\": \"literal_error\",\n                            \"loc\": (\"body\", \"messages\", i, \"role\"),\n                            \"msg\": \"'role' must be user or assistant\",\n                        }\n                    ]\n                )\n\n            if message.get(\"role\") != next_role:\n                # Wrong order\n                raise RequestValidationError(\n                    errors=[\n                        {\n                            \"type\": \"literal_error\",\n                            \"loc\": (\"body\", \"messages\", i, \"role\"),\n                            \"msg\": (\n                                \"messages role order must alternate \"\n                                \"between user and assistant\"\n                            ),\n                        }\n                    ]\n                )\n\n            next_role = \"assistant\" if next_role == \"user\" else \"user\"\n\n        if next_role != \"assistant\":\n            # Last message is not user\n            raise RequestValidationError(\n                errors=[","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/agent/api/schemas/base_inputs.py#L49-L85","documentation":"validate_messages_params enforces that roles strictly alternate user/assistant. When messages[i].role does not equal the expected next_role (starting from user and flipping each step), it raises RequestValidationError with loc body.messages[i].role and msg \"messages role order must alternate between user and assistant\".","triggerScenarios":"Sending consecutive messages of the same role (two user messages in a row, or two assistant messages), or starting the array with an assistant message when the validator expects the first role to be user.","commonSituations":"Clients appending multiple user turns from retries or multi-input UIs; chat histories imported from other systems that allow consecutive same-role turns; branching regeneration code that duplicates assistant replies.","solutions":["Merge consecutive same-role messages into one message (concatenate content) before sending.","Ensure the array starts with a user message and alternates strictly.","Insert a minimal assistant acknowledgment between consecutive user turns if merging is not possible.","Client-side, run the same alternation check on the history and normalize it before the request."],"exampleFix":"# before\nmessages = [\n  {\"role\": \"user\", \"content\": \"a\"},\n  {\"role\": \"user\", \"content\": \"b\"},\n]\n\n# after\nmessages = [{\"role\": \"user\", \"content\": \"a\\nb\"}]  # or alternate with assistant","handlingStrategy":"validation","validationCode":"def normalize_alternating(messages):\n    out = []\n    for m in messages:\n        if out and out[-1][\"role\"] == m[\"role\"]:\n            out[-1][\"content\"] += \"\\n\" + m[\"content\"]\n        else:\n            out.append(dict(m))\n    return out","typeGuard":"def is_alternating(messages) -> bool:\n    expected = \"user\"\n    for m in messages:\n        if m.get(\"role\") != expected:\n            return False\n        expected = \"assistant\" if expected == \"user\" else \"user\"\n    return True","tryCatchPattern":"try:\n    r = client.post(url, json={\"messages\": messages})\n    r.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if 'must alternate' in e.response.text:\n        messages = normalize_alternating(messages)\n        r = client.post(url, json={\"messages\": messages})","preventionTips":["Normalize chat history (merge consecutive same-role turns) before every request.","Guarantee history starts with a user message.","Unit-test history builders for alternation invariants.","Handle retry/regeneration flows so duplicate assistant turns are collapsed."],"tags":["api","validation","openai-compatible","fastapi"],"backgroundTag":"invalid-argument-format","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}