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 switchView on GitHub (pinned to 027ecf0a37)
Solutions
- After each Gemini reply, only send the next request once a new user message or function result has been appended
- In function-calling flows, always append the ToolCallResult/FunctionResponse message before calling GenerateReplyAsync again
- 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
- Always append tool/function results before the next GenerateReplyAsync in function-calling flows
- Never send back a history ending with the model's own reply
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
- The first message must be from the user or function
- Message type {m.GetType()} is not supported.
- Failed to generate content. Status code: {response.StatusCod
- Unsupported message type: {reply.GetType()}
- The response should contain either text or tool calls.
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/a833257a36fb7b9a.
Report an issue: GitHub.