microsoft/autogen · error · InvalidOperationException

Invalid Role

Error message

Invalid Role

What it means

Thrown by ProcessTextMessage when a TextMessage has From == null and a Role that is neither User nor Assistant (System is handled earlier, so this typically means Role.Tool or an unexpected role). The connector cannot decide whether to emit a ChatRequestUserMessage or ChatRequestAssistantMessage for an anonymous sender with an ambiguous role.

Source

Thrown at dotnet/src/AutoGen.OpenAI.V1/Middleware/OpenAIChatRequestMessageConnector.cs:286

    private IEnumerable<ChatRequestMessage> ProcessTextMessage(IAgent agent, TextMessage message)
    {
        if (message.Role == Role.System)
        {
            return [new ChatRequestSystemMessage(message.Content) { Name = message.From }];
        }

        if (agent.Name == message.From)
        {
            return [new ChatRequestAssistantMessage(message.Content) { Name = agent.Name }];
        }
        else
        {
            return message.From switch
            {
                null when message.Role == Role.User => [new ChatRequestUserMessage(message.Content)],
                null when message.Role == Role.Assistant => [new ChatRequestAssistantMessage(message.Content)],
                null => throw new InvalidOperationException("Invalid Role"),
                _ => [new ChatRequestUserMessage(message.Content) { Name = message.From }]
            };
        }
    }

    private IEnumerable<ChatRequestMessage> ProcessImageMessage(IAgent agent, ImageMessage message)
    {
        if (agent.Name == message.From)
        {
            // image message from assistant is not supported
            throw new ArgumentException("ImageMessage is not supported when message.From is the same with agent");
        }

        var imageContentItem = this.CreateChatMessageImageContentItemFromImageMessage(message);
        return [new ChatRequestUserMessage([imageContentItem]) { Name = message.From }];
    }

    private IEnumerable<ChatRequestMessage> ProcessMultiModalMessage(IAgent agent, MultiModalMessage message)

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Set From on the TextMessage (any non-null value routes to ChatRequestUserMessage)
  2. Use Role.User or Role.Assistant for anonymous text messages
  3. Represent tool outputs as ToolCallResultMessage rather than TextMessage(Role.Tool, ...)

Example fix

// before
var msg = new TextMessage(Role.Tool, "tool output"); // From == null -> throws

// after
var msg = new ToolCallResultMessage(toolCallId, "tool output", functionName, from: "Tool");
Defensive patterns

Strategy: validation

Validate before calling

if (textMessage.From is null && textMessage.Role is not (Role.User or Role.Assistant or Role.System)) { /* set From or fix the role */ }

Type guard

static bool HasValidAnonymousRole(TextMessage m) => m.From is not null || m.Role is Role.User or Role.Assistant or Role.System;

Try / catch

catch (InvalidOperationException ex) when (ex.Message == "Invalid Role") { /* normalize role/from and retry once */ }

Prevention

When it happens

Trigger: Constructing new TextMessage(Role.Tool, ...) (or any non-User/Assistant/System role) without setting From, then sending it to an agent using this connector.

Common situations: Tool-result messages mistakenly created as TextMessage with Role.Tool instead of ToolCallResultMessage; role enums extended with custom values; messages built by generic code that leaves From unset.

Related errors


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