{"record":{"id":"8ea677b29134578f","repo":"BerriAI/litellm","slug":"invalid-first-message-should-always-start-with-r","errorCode":null,"errorMessage":"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, ","messagePattern":"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, ","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/prompt_templates/factory.py","lineNumber":1082,"sourceCode":"\n        assistant_content = []\n        ## MERGE CONSECUTIVE ASSISTANT CONTENT ##\n        while msg_i < len(messages) and messages[msg_i][\"role\"] == \"assistant\":\n            assistant_text = messages[msg_i].get(\"content\") or \"\"  # either string or none\n            if messages[msg_i].get(\"tool_calls\", []):  # support assistant tool invoke conversion\n                assistant_text += convert_to_anthropic_tool_invoke_xml(messages[msg_i][\"tool_calls\"])\n\n            assistant_content.append({\"type\": \"text\", \"text\": assistant_text})\n            msg_i += 1\n\n        if assistant_content:\n            new_messages.append({\"role\": \"assistant\", \"content\": assistant_content})\n\n    if not new_messages or new_messages[0][\"role\"] != \"user\":\n        if litellm.modify_params:\n            new_messages.insert(0, {\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": \".\"}]})\n        else:\n            raise Exception(\n                \"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, \"\n            )\n\n    if new_messages[-1][\"role\"] == \"assistant\":\n        for content in new_messages[-1][\"content\"]:\n            if isinstance(content, dict) and content[\"type\"] == \"text\":\n                content[\"text\"] = content[\"text\"].rstrip()  # no trailing whitespace for final assistant message\n\n    return new_messages\n\n\n# ------------------------------------------------------------------------------\n\n\ndef _azure_tool_call_invoke_helper(\n    function_call_params: ChatCompletionToolCallFunctionChunk,\n) -> ChatCompletionToolCallFunctionChunk | None:\n    \"\"\"","sourceCodeStart":1064,"sourceCodeEnd":1100,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/litellm_core_utils/prompt_templates/factory.py#L1064-L1100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Ensure the first message after system has role='user'","Or enable litellm.modify_params = True (litellm_settings: modify_params: true on the proxy) to auto-insert the placeholder","Reorder or merge messages before sending so a user turn leads"],"exampleFix":"# before\nmessages = [{\"role\": \"assistant\", \"content\": \"Sure, here is...\"}]\n\n# after\nmessages = [\n    {\"role\": \"user\", \"content\": \"Continue this text\"},\n    {\"role\": \"assistant\", \"content\": \"Sure, here is...\"},\n]\n\n# alternative: import litellm; litellm.modify_params = True","handlingStrategy":"validation","validationCode":"def ensure_leading_user_message(messages: list[dict]) -> list[dict]:\n    \"\"\"Insert a placeholder user turn if the first non-system message is not 'user'.\"\"\"\n    idx = next((i for i, m in enumerate(messages) if m.get(\"role\") != \"system\"), len(messages))\n    if idx == len(messages) or messages[idx].get(\"role\") != \"user\":\n        return messages[:idx] + [{\"role\": \"user\", \"content\": \".\"}] + messages[idx:]\n    return messages\n\n# messages = ensure_leading_user_message(messages)  # before calling anthropic models","typeGuard":null,"tryCatchPattern":"try:\n    litellm.completion(model=\"anthropic/claude-...\", messages=messages)\nexcept Exception as e:\n    if \"Should always start with 'role'='user' for Anthropic\" in str(e):\n        messages = ensure_leading_user_message(messages)\n        litellm.completion(model=\"anthropic/claude-...\", messages=messages)\n    else:\n        raise","preventionTips":["Always construct Anthropic conversations starting with a user turn after system","Set litellm.modify_params = True (or proxy litellm_settings) when replaying arbitrary histories","When trimming history, never drop the first user message while keeping later turns"],"tags":["anthropic","messages","validation","message-order"],"backgroundTag":"message-order-validation","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}