BerriAI/litellm · error · Exception

Invalid first message. Should always start with…

Error message

Invalid first message. Should always start with 'role'='user' for Anthropic. System prompt is sent separately for Anthropic. set 'litellm.modify_params = True' or 'litellm_settings:modify_params = True' on proxy, to insert a placeholder user message - '.' as the first message, 

What it means

Anthropic requires the first non-system message to have role='user' (system is sent as a separate parameter). After converting messages, if the list is empty or the first entry is not a user message, litellm raises - unless litellm.modify_params is enabled, in which case it inserts a placeholder user message '.' automatically.

Solutions

  1. Ensure the first message after system has role='user'
  2. Or enable litellm.modify_params = True (litellm_settings: modify_params: true on the proxy) to auto-insert the placeholder
  3. Reorder or merge messages before sending so a user turn leads

Example fix

# before
messages = [{"role": "assistant", "content": "Sure, here is..."}]

# after
messages = [
    {"role": "user", "content": "Continue this text"},
    {"role": "assistant", "content": "Sure, here is..."},
]

# alternative: import litellm; litellm.modify_params = True
Defensive patterns

Strategy: validation

Validate before calling

def ensure_leading_user_message(messages: list[dict]) -> list[dict]:
    """Insert a placeholder user turn if the first non-system message is not 'user'."""
    idx = next((i for i, m in enumerate(messages) if m.get("role") != "system"), len(messages))
    if idx == len(messages) or messages[idx].get("role") != "user":
        return messages[:idx] + [{"role": "user", "content": "."}] + messages[idx:]
    return messages

# messages = ensure_leading_user_message(messages)  # before calling anthropic models

Try / catch

try:
    litellm.completion(model="anthropic/claude-...", messages=messages)
except Exception as e:
    if "Should always start with 'role'='user' for Anthropic" in str(e):
        messages = ensure_leading_user_message(messages)
        litellm.completion(model="anthropic/claude-...", messages=messages)
    else:
        raise

Prevention

When it happens

Trigger: messages starting with an assistant turn (prefill) and no preceding user turn; starting with a tool message; aggressive history trimming or conversion filtering that removes every user message leaving new_messages empty.

Common situations: Assistant-prefill patterns ported from OpenAI-style flows; agent loops that resume with an assistant summary; sanitizers dropping user messages (e.g. empty-content filters).

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/8ea677b29134578f. Report an issue: GitHub.

Appendix: source

Thrown at litellm/litellm_core_utils/prompt_templates/factory.py:1082

        assistant_content = []
        ## MERGE CONSECUTIVE ASSISTANT CONTENT ##
        while msg_i < len(messages) and messages[msg_i]["role"] == "assistant":
            assistant_text = messages[msg_i].get("content") or ""  # either string or none
            if messages[msg_i].get("tool_calls", []):  # support assistant tool invoke conversion
                assistant_text += convert_to_anthropic_tool_invoke_xml(messages[msg_i]["tool_calls"])

            assistant_content.append({"type": "text", "text": assistant_text})
            msg_i += 1

        if assistant_content:
            new_messages.append({"role": "assistant", "content": assistant_content})

    if not new_messages or new_messages[0]["role"] != "user":
        if litellm.modify_params:
            new_messages.insert(0, {"role": "user", "content": [{"type": "text", "text": "."}]})
        else:
            raise Exception(
                "Invalid first message. Should always start with 'role'='user' for Anthropic. System prompt is sent separately for Anthropic. set 'litellm.modify_params = True' or 'litellm_settings:modify_params = True' on proxy, to insert a placeholder user message - '.' as the first message, "
            )

    if new_messages[-1]["role"] == "assistant":
        for content in new_messages[-1]["content"]:
            if isinstance(content, dict) and content["type"] == "text":
                content["text"] = content["text"].rstrip()  # no trailing whitespace for final assistant message

    return new_messages


# ------------------------------------------------------------------------------


def _azure_tool_call_invoke_helper(
    function_call_params: ChatCompletionToolCallFunctionChunk,
) -> ChatCompletionToolCallFunctionChunk | None:
    """

View on GitHub (pinned to 77b7c6c40c)