microsoft/autogen · error · InvalidOperationException

system message has already been set and only one system mess

Error message

system message has already been set and only one system message is supported. "system" role for input messages in the Message

What it means

AutoGen.Anthropic's AnthropicClientAgent.BuildMessages throws InvalidOperationException when an input message contains a ChatMessage whose Role == "system". The agent already injects the system message (from its systemMessage parameter) at the top of the Anthropic request, and the Anthropic API allows only one system prompt, so a second system-role message in the conversation history is rejected client-side.

Source

Thrown at dotnet/src/AutoGen.Anthropic/Agent/AnthropicClientAgent.cs:90

            Tools = _tools?.ToList(),
            ToolChoice = _toolChoice ?? (_tools is { Length: > 0 } ? ToolChoice.Auto : null),
            StopSequences = options?.StopSequence?.ToArray(),
        };

        chatCompletionRequest.Messages = BuildMessages(messages);

        return chatCompletionRequest;
    }

    private List<ChatMessage> BuildMessages(IEnumerable<IMessage> messages)
    {
        List<ChatMessage> chatMessages = new();
        foreach (IMessage? message in messages)
        {
            switch (message)
            {
                case IMessage<ChatMessage> chatMessage when chatMessage.Content.Role == "system":
                    throw new InvalidOperationException(
                        "system message has already been set and only one system message is supported. \"system\" role for input messages in the Message");

                case IMessage<ChatMessage> chatMessage:
                    chatMessages.Add(chatMessage.Content);
                    break;

                default:
                    throw new ArgumentException($"Unexpected message type: {message?.GetType()}");
            }
        }

        // merge messages with the same role
        // fixing #2884
        var mergedMessages = chatMessages.Aggregate(new List<ChatMessage>(), (acc, message) =>
        {
            if (acc.Count > 0 && acc.Last().Role == message.Role)
            {
                acc.Last().Content.AddRange(message.Content);

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Remove system-role messages from the input list; put the instruction in the agent's systemMessage constructor parameter instead.
  2. If you aggregate history, filter before sending: messages.Where(m => !(m is IMessage<ChatMessage> c && c.Content.Role == "system")).
  3. In multi-agent flows, have other agents speak as "user"/"assistant" (or AutoGen TextMessage), never "system".
  4. Upgrade AutoGen.Anthropic if a newer version tolerates/merges system messages instead of throwing.

Example fix

// before
var agent = new AnthropicClientAgent(client, "assistant", systemMessage: "You are helpful.");
await agent.SendAsync(new MessageEnvelope<ChatMessage>(new ChatMessage { Role = "system", Content = [new TextContent { Text = "Always answer briefly." }] })); // throws

// after
var agent = new AnthropicClientAgent(client, "assistant", systemMessage: "You are helpful. Always answer briefly.");
await agent.SendAsync(new MessageEnvelope<ChatMessage>(new ChatMessage { Role = "user", Content = [new TextContent { Text = "hi" }] }));
Defensive patterns

Strategy: type-guard

Validate before calling

var hasSystem = messages.OfType<IMessage<ChatMessage>>().Any(m => m.Content.Role == "system");
if (hasSystem)
{
    messages = messages.Where(m => !(m is IMessage<ChatMessage> c && c.Content.Role == "system"));
    // fold the text into the agent's systemMessage instead
}

Type guard

static bool ContainsSystemMessage(IEnumerable<IMessage> messages) =>
    messages.OfType<IMessage<ChatMessage>>().Any(m => m.Content.Role == "system");

Try / catch

try { await agent.SendAsync(messages); } catch (InvalidOperationException ex) when (ex.Message.Contains("system message")) { messages = messages.Where(m => m is not IMessage<ChatMessage> c || c.Content.Role != "system"); await agent.SendAsync(messages); }

Prevention

When it happens

Trigger: Sending an IMessage<ChatMessage> with Content.Role == "system" in the messages enumerable to AnthropicClientAgent (directly or via a group chat that replays history); using RegisterMessageConnector and re-sending the agent's own stored chat history that includes a system entry; two agents in a group chat where one emits system-role messages.

Common situations: Porting OpenAI sample code to Anthropic while keeping system messages inline in the message list; group-chat middleware that prefixes every turn with a system instruction; replaying a persisted conversation log that recorded the initial system prompt as a regular message.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/abaea475475129dc. Report an issue: GitHub.