microsoft/autogen · error · ArgumentException

The last message must be from the user or function

Error message

The last message must be from the user or function

What it means

Thrown by GeminiChatAgent.BuildChatRequest because the Gemini chat API requires the last message to be from 'user' or 'function' (i.e. the request must end with a turn the model responds to). If the final Content has role 'model' (assistant), ArgumentException is thrown with paramName 'messages'.

Source

Thrown at dotnet/src/AutoGen.Gemini/GeminiChatAgent.cs:179

            IMessage<Content> contentMessage => contentMessage.Content,
            _ => throw new NotSupportedException($"Message type {m.GetType()} is not supported.")
        });

        // there are several rules applies to the messages that can be sent to Gemini in a multi-turn chat
        // - The first message must be from the user or function
        // - The (user|model) roles must alternate e.g. (user, model, user, model, ...)
        // - The last message must be from the user or function

        // check if the first message is from the user
        if (geminiMessages.FirstOrDefault()?.Role != "user" && geminiMessages.FirstOrDefault()?.Role != "function")
        {
            throw new ArgumentException("The first message must be from the user or function", nameof(messages));
        }

        // check if the last message is from the user
        if (geminiMessages.LastOrDefault()?.Role != "user" && geminiMessages.LastOrDefault()?.Role != "function")
        {
            throw new ArgumentException("The last message must be from the user or function", nameof(messages));
        }

        // merge continuous messages with the same role into one message
        var mergedMessages = geminiMessages.Aggregate(new List<Content>(), (acc, message) =>
        {
            if (acc.Count == 0 || acc.Last().Role != message.Role)
            {
                acc.Add(message);
            }
            else
            {
                acc.Last().Parts.AddRange(message.Parts);
            }

            return acc;
        });

        var systemMessage = this.systemMessage switch

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. After each Gemini reply, only send the next request once a new user message or function result has been appended
  2. In function-calling flows, always append the ToolCallResult/FunctionResponse message before calling GenerateReplyAsync again
  3. Trim trailing model-role messages, or append a synthetic user message (e.g. "continue") to satisfy the ordering rule

Example fix

// before:
history.Add(geminiReply); // model role now last
await agent.GenerateReplyAsync(history); // throws
// after:
history.Add(geminiReply);
history.Add(new TextMessage(Role.User, "please continue"));
await agent.GenerateReplyAsync(history);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the request ends on a user or function turn
var last = messages.LastOrDefault() as IMessage<Content>;
if (last?.Content.Role is not ("user" or "function"))
    messages = messages.Append(new TextMessage(Role.User, "please continue")).ToList();

Type guard

static bool EndsOnUserTurn(IEnumerable<IMessage> msgs) =>
    (msgs.LastOrDefault() as IMessage<Content>)?.Content.Role is "user" or "function";

Try / catch

try { await geminiAgent.GenerateReplyAsync(messages, options, ct); }
catch (ArgumentException e) when (e.Message.Contains("last message must be from the user"))
{ /* append pending tool results or a user message, then retry */ }

Prevention

When it happens

Trigger: Appending the assistant's previous reply as the last element of the next request — common when you push the model's response onto the same list you send back, or when a tool-call assistant message is not followed by its function result.

Common situations: Chat loops that send the full history including the last assistant message without a new user/tool message; function-calling flows missing the ToolCallResultMessage after the model's tool-call turn.

Related errors


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