microsoft/autogen · error · InvalidOperationException

ToolCallMessage is not supported as user role in Gemini.

Error message

ToolCallMessage is not supported as user role in Gemini.

What it means

GeminiMessageConnector.ProcessToolCallMessage throws in strict mode when a ToolCallMessage must be rendered with the Gemini 'user' role. ShouldParseAsUser decides the role; a function-call part is only valid as 'model' in the Gemini API, so a tool call attributed to someone other than the Gemini agent itself cannot be represented and strict mode rejects it.

Source

Thrown at dotnet/src/AutoGen.Gemini/Middleware/GeminiMessageConnector.cs:317

            functionCallResultParts.Add(part);
        }

        var content = new Content
        {
            Parts = { functionCallResultParts },
            Role = "function",
        };

        return [MessageEnvelope.Create(content, toolCallResultMessage.From)];
    }

    private IEnumerable<IMessage> ProcessToolCallMessage(ToolCallMessage toolCallMessage, IAgent agent)
    {
        var shouldParseAsUser = ShouldParseAsUser(toolCallMessage, agent);
        if (strictMode && shouldParseAsUser)
        {
            throw new InvalidOperationException("ToolCallMessage is not supported as user role in Gemini.");
        }

        var functionCallParts = new List<Part>();
        foreach (var toolCall in toolCallMessage.ToolCalls)
        {
            var part = new Part
            {
                FunctionCall = new FunctionCall
                {
                    Name = toolCall.FunctionName,
                    Args = Struct.Parser.ParseJson(toolCall.FunctionArguments),
                }
            };

            functionCallParts.Add(part);
        }
        var content = new Content
        {

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Set message.From to the Gemini agent's name before sending its own ToolCallMessage history back to it.
  2. Convert foreign ToolCallMessages into ToolCallResultMessages or a TextMessage summary before they reach a strict Gemini agent.
  3. Disable strict mode (strictMode:false) so the connector maps it leniently.
  4. Reorder group-chat routing so tool-call/result pairs are only sent to the agent that issued them.

Example fix

// before
var foreign = new ToolCallMessage(calls, from: "openai-assistant");
await geminiAgent.InitiateChatAsync(null, null, foreign); // strict connector

// after
var summary = new TextMessage(Role.User, "openai-assistant called: " + string.Join(",", calls.Select(c => c.FunctionName)), from: "openai-assistant");
await geminiAgent.SendAsync(summary);
Defensive patterns

Strategy: validation

Validate before calling

if (msg is ToolCallMessage tcm && tcm.From != geminiAgent.Name)
{
    msg = new TextMessage(Role.User, "tool calls: " + string.Join(",", tcm.ToolCalls.Select(t => t.FunctionName)), from: tcm.From);
}

Prevention

When it happens

Trigger: strictMode:true plus a ToolCallMessage whose From differs from the Gemini agent's name (e.g. a tool-call message produced by another agent in a group chat) reaching the Gemini agent.

Common situations: Multi-agent group chats where an assistant agent's ToolCallMessage is replayed to the Gemini agent as context; middleware forwarding tool calls between agents with strict connectors.

Related errors


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