-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Description
Description
Claude 4.6 models (Sonnet 4.6, Opus 4.6) return 400 Bad Request errors when used with CrewAI agents. Anthropic made a breaking API change: Claude 4.6 no longer supports assistant message prefilling, and any request with a trailing assistant message is rejected.
Root Cause
CrewAgentExecutor._append_message() appends every LLM response as role: "assistant" (crew_agent_executor.py#L273):
self._append_message(formatted_answer.text) # role defaults to "assistant"This builds conversations with consecutive assistant messages:
user: [system prompt + task]
assistant: [LLM response with tool call]
assistant: [tool result + observation] ← consecutive assistant
assistant: [next iteration] ← consecutive assistant
Claude 4.6 interprets the trailing assistant message as a prefill attempt and rejects the request. Earlier Claude models (3.5, 4.5) tolerated this, but 4.6 is strict.
Steps to Reproduce
- Create a CrewAI agent with
llm=LLM(model="anthropic/claude-sonnet-4-6-20250514") - Give it a task that requires tool use (multiple iterations)
- The agent fails on the second LLM call with:
400 Bad Request: Prefilling assistant messages is no longer supported
Expected Behavior
CrewAI should normalize the message history before sending to the LLM — either by:
- Merging consecutive same-role messages
- Inserting a
usermessage between consecutiveassistantmessages - Using
_format_model_specific_messages()to handle this (it already has Mistral/Ollama handling but not Anthropic)
Environment
- CrewAI version: 1.6.1 (also reproduced conceptually against 1.10.1 source)
- Python: 3.13
- LLM provider: OpenRouter (LiteLLM path)
- Models affected:
anthropic/claude-sonnet-4-6-20250514,anthropic/claude-opus-4-6-20250514
Related Issues
- [BUG] Error with alternating messages structure in instruct mode #1454 (alternating messages in instruct mode — fixed via LiteLLM
ensure_alternating_roles, but that param is no longer available) - fix: ensure proper message formatting for Anthropic models #2063 (Anthropic message formatting — fixed first-message ordering but not consecutive assistant messages)
- [FEATURE] Enable CrewAI agents to use Claude's extended thinking #3964 (extended thinking — fixed thinking block preservation)
- External: livekit/agents#4907, strands-agents/sdk-python#1694
Suggested Fix
Add Anthropic handling to _format_model_specific_messages() in llm.py (around line 1508) to merge consecutive assistant messages, similar to the existing Mistral/Ollama handling:
# Handle Anthropic models — merge consecutive same-role messages
if self.is_anthropic:
merged = [messages[0]]
for msg in messages[1:]:
if msg["role"] == merged[-1]["role"]:
merged[-1]["content"] += "\n\n" + str(msg["content"])
else:
merged.append(msg)
messages = mergedWorkaround
Using a LiteLLM input callback to merge consecutive messages before they reach the API:
import litellm
def merge_consecutive_messages(kwargs, **_):
messages = kwargs.get("messages", [])
if len(messages) < 2:
return
merged = [messages[0]]
for msg in messages[1:]:
if msg["role"] == merged[-1]["role"]:
merged[-1]["content"] += "\n\n" + str(msg["content"])
else:
merged.append(msg)
kwargs["messages"] = merged
litellm.input_callback = [merge_consecutive_messages]