microsoft/semantic-kernel · error · InvalidOperationException
Message role must be either Assistant or User.
Error message
Message role must be either Assistant or User.
What it means
Thrown when building the Bedrock agent conversation history, this error rejects any ChatMessageContent whose Role is neither AuthorRole.Assistant nor AuthorRole.User. The Bedrock Agent runtime conversation API only models a two-party dialogue (assistant vs user), so system, tool, or custom-role messages cannot be mapped. The check is applied to every message in the history except the last one (the current user turn).
Source
Thrown at dotnet/src/Agents/Bedrock/BedrockAgentChannel.cs:227
SessionState sessionState = new();
// We don't take the last message as it needs to be sent separately in another parameter.
if (this._history.Count > 1)
{
sessionState.ConversationHistory = new()
{
Messages = []
};
foreach (var message in this._history.Take(this._history.Count - 1))
{
if (message.Content is null)
{
throw new InvalidOperationException("Message content cannot be null.");
}
if (message.Role != AuthorRole.Assistant && message.Role != AuthorRole.User)
{
throw new InvalidOperationException("Message role must be either Assistant or User.");
}
sessionState.ConversationHistory.Messages.Add(new()
{
Role = message.Role == AuthorRole.Assistant
? Amazon.BedrockAgentRuntime.ConversationRole.Assistant
: Amazon.BedrockAgentRuntime.ConversationRole.User,
Content = [
new Amazon.BedrockAgentRuntime.Model.ContentBlock()
{
Text = message.Content,
},
],
});
}
}
return sessionState;View on GitHub (pinned to c028a0c7dc)
Solutions
- Filter the history before passing it to the Bedrock agent: remove or convert any message whose Role is not AuthorRole.User or AuthorRole.Assistant.
- Move system instructions into the Bedrock agent's instruction/agent configuration rather than into the conversation history.
- If a Tool role message exists, ensure function results are routed through the Bedrock session-state ReturnControl path rather than added as history messages.
- Verify the thread history only contains alternating User/Assistant turns prior to the current invocation.
Example fix
// before
history.Add(new ChatMessageContent(AuthorRole.System, "You are a helpful agent."));
agent.InvokeAsync(history, thread);
// after — put instructions in the agent definition, not the history
var agent = await bedrockAgentClient.CreateOrUpdateAgentAsync(
new CreateAgentRequest { ... AgentResourceRoleArn = ..., Instruction = "You are a helpful agent." });
agent.InvokeAsync(history, thread); // history contains only User/Assistant messages Defensive patterns
Strategy: validation
Validate before calling
// Before invoking the Bedrock agent, filter history to allowed roles
using Microsoft.SemanticKernel.ChatCompletion;
ChatHistory filtered = new();
foreach (var m in history)
{
if (m.Role == AuthorRole.User || m.Role == AuthorRole.Assistant)
filtered.Add(m);
// else: drop System/Tool/custom-role messages
}
// pass `filtered` to the Bedrock agent invocation Type guard
static bool IsBedrockSupportedRole(ChatMessageContent m) =>
m.Role == AuthorRole.User || m.Role == AuthorRole.Assistant; Try / catch
try { await foreach (var r in agent.InvokeAsync(filtered, thread, ct)) { ... } }
catch (InvalidOperationException ex) when (ex.Message.Contains("Message role"))
{
logger.LogError("Unsupported message role in history. Filter to User/Assistant only.");
throw;
} Prevention
- Keep a separate ChatHistory for Bedrock agents containing only User and Assistant turns.
- Put system instructions in the Bedrock agent's instruction config, not in conversation history.
- Write a unit test asserting all messages in a Bedrock-bound history are User or Assistant role.
When it happens
Trigger: Calling InvokeAsync/InvokeStreamingAsync on a BedrockAgent with an AgentThread whose history contains a System message, a Tool message, or any message with a non-standard AuthorRole. Also triggered when a ChatHistory is shared between a different connector (that accepted System messages) and a Bedrock agent.
Common situations: Reusing a ChatHistory built for OpenAI/Azure connectors where System messages are standard; prepending a system prompt to the thread; tool/function result messages that carry AuthorRole.Tool; messages created with new AuthorRole("system").
Related errors
- No function results were returned.
- Invalid parameter type for function {agentToolDefinition.Id}
- The option key '{key}' is required for a Bedrock function pa
- Unsupported parameter type: {typeString}
- runtimeClient
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/9b100a7b3f272a79.
Report an issue: GitHub.