microsoft/semantic-kernel · error · InvalidOperationException

Bedrock agents must be invoked with a user message

Error message

Bedrock agents must be invoked with a user message

What it means

Thrown by BedrockAgent.ExtractUserMessage when the message's role is not AuthorRole.User. Bedrock agents expect the final/invoking message to be a user message; assistant or system messages in that slot are rejected. This validates the last message before sending it as the agent input.

Solutions

  1. Ensure the last message in the collection has Role == AuthorRole.User.
  2. If the latest turn is an assistant reply, append a user turn (even a continuation prompt) before invoking.

Example fix

// before
var messages = new List<ChatMessageContent>
{
    new(AuthorRole.User, "hi"),
    new(AuthorRole.Assistant, "hello!"), // last is assistant -> throws 177
};
await agent.InvokeAsync(messages);

// after
messages.Add(new(AuthorRole.User, "tell me more"));
await agent.InvokeAsync(messages);
Defensive patterns

Strategy: type-guard

Validate before calling

var last = messages.Last();
if (!last.Role.Equals(AuthorRole.User))
    throw new InvalidOperationException("The final message must be a user message for Bedrock agents.");

Type guard

static bool LastIsUser(ICollection<ChatMessageContent> m) =>
    m.Count > 0 && m.Last().Role.Equals(AuthorRole.User);

Try / catch

try { await agent.InvokeAsync(messages); }
catch (InvalidOperationException ex) when (ex.Message.Contains("user message"))
{ /* append a user message as the last entry and retry */ }

Prevention

When it happens

Trigger: The message passed as the primary input to ExtractUserMessage has Role equal to Assistant, System, Tool, or anything other than User.

Common situations: Caller appended an assistant follow-up as the last message by mistake; message history ordering put a non-user message last; tool/result message placed in the user slot.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/4cd278555d47d743. Report an issue: GitHub.

Appendix: source

Thrown at dotnet/src/Agents/Bedrock/BedrockAgent.cs:550

        string agentAlias = WorkingDraftAgentAlias;
        bool enableTrace = false;
        if (options is BedrockAgentInvokeOptions bedrockOption)
        {
            agentAlias = bedrockOption.AgentAliasId ?? WorkingDraftAgentAlias;
            enableTrace = bedrockOption.EnableTrace;
        }

        var invokeRequest = createRequest();
        invokeRequest.AgentAliasId = agentAlias;
        invokeRequest.EnableTrace = enableTrace;
        return invokeRequest;
    }

    private string ExtractUserMessage(ChatMessageContent chatMessageContent)
    {
        if (!chatMessageContent.Role.Equals(AuthorRole.User))
        {
            throw new InvalidOperationException("Bedrock agents must be invoked with a user message");
        }

        return chatMessageContent.Content ?? "";
    }

    private SessionState ExtractSessionState(ICollection<ChatMessageContent> messages)
    {
        // If there is more than one message provided, add all but the last message to the session state
        SessionState sessionState = new();
        if (messages.Count > 1)
        {
            List<Amazon.BedrockAgentRuntime.Model.Message> messageHistory = [];
            for (int i = 0; i < messages.Count - 1; i++)
            {
                var currentMessage = messages.ElementAt(i);
                messageHistory.Add(this.ToBedrockMessage(currentMessage));
            }

View on GitHub (pinned to c028a0c7dc)