{"record":{"id":"9b100a7b3f272a79","repo":"microsoft/semantic-kernel","slug":"message-role-must-be-either-assistant-or-user","errorCode":null,"errorMessage":"Message role must be either Assistant or User.","messagePattern":"Message role must be either Assistant or User\\.","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Agents/Bedrock/BedrockAgentChannel.cs","lineNumber":227,"sourceCode":"        SessionState sessionState = new();\n\n        // We don't take the last message as it needs to be sent separately in another parameter.\n        if (this._history.Count > 1)\n        {\n            sessionState.ConversationHistory = new()\n            {\n                Messages = []\n            };\n\n            foreach (var message in this._history.Take(this._history.Count - 1))\n            {\n                if (message.Content is null)\n                {\n                    throw new InvalidOperationException(\"Message content cannot be null.\");\n                }\n                if (message.Role != AuthorRole.Assistant && message.Role != AuthorRole.User)\n                {\n                    throw new InvalidOperationException(\"Message role must be either Assistant or User.\");\n                }\n\n                sessionState.ConversationHistory.Messages.Add(new()\n                {\n                    Role = message.Role == AuthorRole.Assistant\n                        ? Amazon.BedrockAgentRuntime.ConversationRole.Assistant\n                        : Amazon.BedrockAgentRuntime.ConversationRole.User,\n                    Content = [\n                        new Amazon.BedrockAgentRuntime.Model.ContentBlock()\n                        {\n                            Text = message.Content,\n                        },\n                    ],\n                });\n            }\n        }\n\n        return sessionState;","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/dotnet/src/Agents/Bedrock/BedrockAgentChannel.cs#L209-L245","documentation":"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).","triggerScenarios":"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.","commonSituations":"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\").","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."],"exampleFix":"// before\nhistory.Add(new ChatMessageContent(AuthorRole.System, \"You are a helpful agent.\"));\nagent.InvokeAsync(history, thread);\n\n// after — put instructions in the agent definition, not the history\nvar agent = await bedrockAgentClient.CreateOrUpdateAgentAsync(\n    new CreateAgentRequest { ... AgentResourceRoleArn = ..., Instruction = \"You are a helpful agent.\" });\nagent.InvokeAsync(history, thread); // history contains only User/Assistant messages","handlingStrategy":"validation","validationCode":"// Before invoking the Bedrock agent, filter history to allowed roles\nusing Microsoft.SemanticKernel.ChatCompletion;\n\nChatHistory filtered = new();\nforeach (var m in history)\n{\n    if (m.Role == AuthorRole.User || m.Role == AuthorRole.Assistant)\n        filtered.Add(m);\n    // else: drop System/Tool/custom-role messages\n}\n// pass `filtered` to the Bedrock agent invocation","typeGuard":"static bool IsBedrockSupportedRole(ChatMessageContent m) =>\n    m.Role == AuthorRole.User || m.Role == AuthorRole.Assistant;","tryCatchPattern":"try { await foreach (var r in agent.InvokeAsync(filtered, thread, ct)) { ... } }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Message role\"))\n{\n    logger.LogError(\"Unsupported message role in history. Filter to User/Assistant only.\");\n    throw;\n}","preventionTips":["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."],"tags":["bedrock","agents","message-role","validation","csharp"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}