meta-llama/llama · error · AssertionError

Last message must be from user, got {dialog[-1]['role']}

Error message

Last message must be from user, got {dialog[-1]['role']}

What it means

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.

Source

Thrown at llama/generation.py:354

            ), (
                "model only supports 'system', 'user' and 'assistant' roles, "
                "starting with 'system', then 'user' and alternating (u/a/u/a/u...)"
            )
            dialog_tokens: List[int] = sum(
                [
                    self.tokenizer.encode(
                        f"{B_INST} {(prompt['content']).strip()} {E_INST} {(answer['content']).strip()} ",
                        bos=True,
                        eos=True,
                    )
                    for prompt, answer in zip(
                        dialog[::2],
                        dialog[1::2],
                    )
                ],
                [],
            )
            assert (
                dialog[-1]["role"] == "user"
            ), f"Last message must be from user, got {dialog[-1]['role']}"
            dialog_tokens += self.tokenizer.encode(
                f"{B_INST} {(dialog[-1]['content']).strip()} {E_INST}",
                bos=True,
                eos=False,
            )
            prompt_tokens.append(dialog_tokens)

        generation_tokens, generation_logprobs = self.generate(
            prompt_tokens=prompt_tokens,
            max_gen_len=max_gen_len,
            temperature=temperature,
            top_p=top_p,
            logprobs=logprobs,
        )
        if logprobs:
            return [

View on GitHub (pinned to 689c7f261b)

Solutions

  1. 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
  2. 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
  3. Validate every dialog in the batch before submission; one bad dialog aborts the entire chat_completion call
  4. In UI loops, rebuild the dialog as history pairs + the fresh user question rather than replaying the raw transcript

Example fix

# before
dialog = [
  {"role": "user", "content": "hello"},
  {"role": "assistant", "content": "Hi! How can I help?"},
]
llama.chat_completion([dialog], max_gen_len=64)  # AssertionError

# after
dialog = [
  {"role": "user", "content": "hello"},
  {"role": "assistant", "content": "Hi! How can I help?"},
  {"role": "user", "content": "what is 2+2?"},
]
llama.chat_completion([dialog], max_gen_len=64)
Defensive patterns

Strategy: validation

Validate before calling

def dialog_ends_with_user(dialog):
    return bool(dialog) and dialog[-1]["role"] == "user"

assert all(dialog_ends_with_user(d) for d in dialogs), "every dialog must end with a user message"

Type guard

def ends_with_user(dialog) -> bool:
    return isinstance(dialog, list) and len(dialog) > 0 and dialog[-1].get("role") == "user"

Try / catch

try:
    result = llama.chat_completion(dialogs, max_gen_len=128)
except AssertionError as e:
    if "Last message must be from user" in str(e):
        dialogs = [d if d[-1]["role"] == "user" else d + [{"role": "user", "content": "continue"}] for d in dialogs]
        result = llama.chat_completion(dialogs, max_gen_len=128)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: - 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. - Multi-turn UIs that keep the dialog list state after each generation and resubmit without appending the new user message. - Preprocessing that appends a closing system/guardrail message at the end of the conversation. - Batch pipelines where some dialogs end with 'assistant' and others with 'user', failing the whole batch.

Related errors


AI-assisted analysis of meta-llama/llama@689c7f261b (2026-08-15). Data as JSON: /api/errors/3090a6d8c94730a9. Report an issue: GitHub.