Fixing Anthropic 400: roles must alternate between user and assistant

beginner🧠 AI Tools2026-06-11| Python/Node.js, Anthropic SDK, Claude 3/3.5 models

Error Message

anthropic.BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages: roles must alternate between "user" and "assistant", but found multiple "user" roles in a row'}}
#anthropic#claude#messages#conversation-history#api

Why the Anthropic API is so strict

Claude expects a perfect back-and-forth rhythm. Unlike some LLMs that let you stack messages, the Anthropic Messages API treats the conversation like a tennis match. You send a message, Claude replies, and then you send another. If you break this user → assistant → user pattern, the API rejects the request immediately.

You'll usually run into this 400 error in one of these three ways:

  • A user sent two messages in a row (double-posting) before the AI responded.
  • You tried to put the "system" prompt inside the messages array.
  • The conversation history starts with an "assistant" message instead of a "user" prompt.

Step-by-Step Fix

1. Move System Messages out of the Array

If you're coming from the OpenAI ecosystem, this is a common trap. In Anthropic's API, the system prompt is a top-level parameter. It should never be an object inside your message history list.

# WRONG: This will cause a 400 error
client.messages.create(
    model="claude-3-5-sonnet-20240620",
    messages=[
        {"role": "system", "content": "You are a helpful assistant"},
        {"role": "user", "content": "Hello!"}
    ]
)

# CORRECT: Use the dedicated system parameter
client.messages.create(
    model="claude-3-5-sonnet-20240620",
    system="You are a helpful assistant",
    messages=[
        {"role": "user", "content": "Hello!"}
    ]
)

2. Merge Consecutive Messages of the Same Role

In real-world chat apps, users often send several messages before the AI can respond. You can't just append these to a list. Instead, you must combine them into a single message object before hitting the API.

Use this Python helper to clean up your history by merging duplicate roles:

def merge_consecutive_messages(messages):
    if not messages:
        return []
    
    merged = []
    for msg in messages:
        # Check if the current role matches the last one we added
        if merged and merged[-1]["role"] == msg["role"]:
            prev_content = merged[-1]["content"]
            curr_content = msg["content"]
            
            # Combine strings with a double newline
            if isinstance(prev_content, str) and isinstance(curr_content, str):
                merged[-1]["content"] = f"{prev_content}\n\n{curr_content}"
            else:
                # Merge complex content blocks like images and text
                if isinstance(prev_content, str):
                    merged[-1]["content"] = [{"type": "text", "text": prev_content}]
                if isinstance(curr_content, str):
                    curr_content = [{"type": "text", "text": curr_content}]
                merged[-1]["content"].extend(curr_content)
        else:
            merged.append(msg)
    return merged

3. Always Start with a "user" Message

Anthropic requires every conversation to begin with the user role. If your database pulls a history that starts with a cached assistant response, the API will fail. Always slice or prepend your list to ensure a user prompt leads the way.

Verification

Before calling the API, print your messages array. It should look like a clean alternating sequence. For example: 100% of the roles must follow the [user, assistant, user, assistant] order. If you see two identical roles touching, your merging logic likely failed.

Practical Tips

  • UI Throttling: Disable the "Send" button while the assistant is thinking to prevent users from stacking messages.
  • Content Blocks: When sending text and an image together, use a single "user" role with multiple content blocks rather than two separate messages.
  • Debugging: Log the JSON payload of your request. It's often easier to spot duplicate roles in a raw log than in complex application code.

Related Error Notes