{"record":{"id":"3090a6d8c94730a9","repo":"meta-llama/llama","slug":"last-message-must-be-from-user-got-dialog-1-r","errorCode":null,"errorMessage":"Last message must be from user, got {dialog[-1]['role']}","messagePattern":"Last message must be from user, got (.+?)","errorType":"validation","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"llama/generation.py","lineNumber":354,"sourceCode":"            ), (\n                \"model only supports 'system', 'user' and 'assistant' roles, \"\n                \"starting with 'system', then 'user' and alternating (u/a/u/a/u...)\"\n            )\n            dialog_tokens: List[int] = sum(\n                [\n                    self.tokenizer.encode(\n                        f\"{B_INST} {(prompt['content']).strip()} {E_INST} {(answer['content']).strip()} \",\n                        bos=True,\n                        eos=True,\n                    )\n                    for prompt, answer in zip(\n                        dialog[::2],\n                        dialog[1::2],\n                    )\n                ],\n                [],\n            )\n            assert (\n                dialog[-1][\"role\"] == \"user\"\n            ), f\"Last message must be from user, got {dialog[-1]['role']}\"\n            dialog_tokens += self.tokenizer.encode(\n                f\"{B_INST} {(dialog[-1]['content']).strip()} {E_INST}\",\n                bos=True,\n                eos=False,\n            )\n            prompt_tokens.append(dialog_tokens)\n\n        generation_tokens, generation_logprobs = self.generate(\n            prompt_tokens=prompt_tokens,\n            max_gen_len=max_gen_len,\n            temperature=temperature,\n            top_p=top_p,\n            logprobs=logprobs,\n        )\n        if logprobs:\n            return [","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/meta-llama/llama/blob/689c7f261b9c5514636ecc3c5fefefcbb3e6eed7/llama/generation.py#L336-L372","documentation":"This assertion in chat_completion (llama/generation.py:354) requires the final message of every dialog to have role 'user'. The loop tokenizes prior user/assistant pairs as history and then encodes the last user message as the open-ended prompt (bos=True, eos=False) for generation; an assistant/system message in last position leaves nothing to generate from, so the library refuses it.","triggerScenarios":"Calling llama.chat_completion(dialogs, ...) where any dialog's last element is {'role': 'assistant', ...} (or a trailing 'system'): e.g. replaying a full conversation including the model's final answer, or appending a system reminder at the end. Note it triggers even when the alternation assert (error 3) passes, e.g. [user, assistant] alone.","commonSituations":"- Feeding complete transcripts (which naturally end with the assistant's reply) back in for evaluation/continuation instead of trimming to end on the user turn.\n- Multi-turn UIs that keep the dialog list state after each generation and resubmit without appending the new user message.\n- Preprocessing that appends a closing system/guardrail message at the end of the conversation.\n- Batch pipelines where some dialogs end with 'assistant' and others with 'user', failing the whole batch.","solutions":["Trim or extend the dialog so it ends with a user message: either drop a trailing assistant message or append the next user turn before calling","If you want the model to continue an assistant reply, append an empty/short user turn like {'role':'user','content':'continue'} — this codebase has no continuation API","Validate every dialog in the batch before submission; one bad dialog aborts the entire chat_completion call","In UI loops, rebuild the dialog as history pairs + the fresh user question rather than replaying the raw transcript"],"exampleFix":"# before\ndialog = [\n  {\"role\": \"user\", \"content\": \"hello\"},\n  {\"role\": \"assistant\", \"content\": \"Hi! How can I help?\"},\n]\nllama.chat_completion([dialog], max_gen_len=64)  # AssertionError\n\n# after\ndialog = [\n  {\"role\": \"user\", \"content\": \"hello\"},\n  {\"role\": \"assistant\", \"content\": \"Hi! How can I help?\"},\n  {\"role\": \"user\", \"content\": \"what is 2+2?\"},\n]\nllama.chat_completion([dialog], max_gen_len=64)","handlingStrategy":"validation","validationCode":"def dialog_ends_with_user(dialog):\n    return bool(dialog) and dialog[-1][\"role\"] == \"user\"\n\nassert all(dialog_ends_with_user(d) for d in dialogs), \"every dialog must end with a user message\"","typeGuard":"def ends_with_user(dialog) -> bool:\n    return isinstance(dialog, list) and len(dialog) > 0 and dialog[-1].get(\"role\") == \"user\"","tryCatchPattern":"try:\n    result = llama.chat_completion(dialogs, max_gen_len=128)\nexcept AssertionError as e:\n    if \"Last message must be from user\" in str(e):\n        dialogs = [d if d[-1][\"role\"] == \"user\" else d + [{\"role\": \"user\", \"content\": \"continue\"}] for d in dialogs]\n        result = llama.chat_completion(dialogs, max_gen_len=128)\n    else:\n        raise","preventionTips":["Keep UI dialog state ending at the user turn: append the model's reply only as history when the next question arrives","Trim trailing assistant/system messages from imported transcripts before inference","Validate the whole batch first — one non-conforming dialog aborts all dialogs in the chat_completion call"],"tags":["llama","chat-completion","dialog-format","input-validation"],"backgroundTag":null,"analyzedSha":"689c7f261b9c5514636ecc3c5fefefcbb3e6eed7","analyzedAt":"2026-08-15T02:36:29.698Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}