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
- 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
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
- 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
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
- Either file_data or file_id must be present in the file…
- Invalid image URL
- kwarg `messages` must be an array of messages that follow…
- Message must be a dictionary
- Message must have a 'content' field
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)