microsoft/semantic-kernel · error · AgentExecutionException

Invalid message role `{message.role.value}`. Allowed roles a

Error message

Invalid message role `{message.role.value}`. Allowed roles are {allowed_message_roles}.

What it means

Before adding a message to an OpenAI Assistant thread, the helper verifies the message role is permitted. allowed_message_roles defaults to [USER, ASSISTANT] when None, and TOOL is always implicitly allowed. Any other role (e.g. SYSTEM, a custom role) raises AgentExecutionException because the OpenAI threads API only accepts user/assistant/tool messages in this flow.

Source

Thrown at python/semantic_kernel/agents/open_ai/assistant_content_generation.py:73

) -> "Message":
    """Class method to add a chat message, callable from class or instance.

    Args:
        client: The client to use for creating the message.
        thread_id: The thread id.
        message: The chat message.
        allowed_message_roles: The allowed message roles.
            Defaults to [AuthorRole.USER, AuthorRole.ASSISTANT] if None.
            Providing an empty list will disallow all message roles.

    Returns:
        Message: The message.
    """
    # Set the default allowed message roles if not provided
    if allowed_message_roles is None:
        allowed_message_roles = [AuthorRole.USER, AuthorRole.ASSISTANT]
    if message.role.value not in allowed_message_roles and message.role != AuthorRole.TOOL:
        raise AgentExecutionException(
            f"Invalid message role `{message.role.value}`. Allowed roles are {allowed_message_roles}."
        )

    message_contents: list[dict[str, Any]] = get_message_contents(message=message)

    return await client.beta.threads.messages.create(
        thread_id=thread_id,
        role="assistant" if message.role == AuthorRole.TOOL else message.role.value,  # type: ignore
        content=message_contents,  # type: ignore
    )


@experimental
def get_message_contents(message: "ChatMessageContent") -> list[dict[str, Any]]:
    """Get the message contents.

    Args:
        message: The message.

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Filter out or convert non-conforming messages before invoking: keep only USER and ASSISTANT (and TOOL) roles.
  2. If you intentionally restrict roles, make sure the messages you submit match allowed_message_roles (or rely on the default).
  3. Move SYSTEM instructions into the assistant's instructions/model config rather than the thread messages.

Example fix

// before
history.add(MessageRole.SYSTEM, "You are a helpful agent")
await assistant.invoke(thread_id=tid, messages=history)  # SYSTEM -> Invalid message role

// after
# put system guidance in the assistant definition, only user/assistant in the thread
history.add(MessageRole.USER, "Summarize the report")
await assistant.invoke(thread_id=tid, messages=history)
Defensive patterns

Strategy: validation

Validate before calling

from semantic_kernel.contents import AuthorRole

ALLOWED = {AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL}

def safe_for_thread(msg) -> bool:
    return msg.role in ALLOWED

Type guard

from semantic_kernel.contents import AuthorRole

def is_thread_safe_role(msg) -> bool:
    return msg.role in (AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL)

Prevention

When it happens

Trigger: Calling get_chat_message_content / the message-create helper with a ChatMessageContent whose role is SYSTEM (or any non-USER/ASSISTANT/TOOL role), or with a role excluded by an explicitly passed allowed_message_roles list.

Common situations: Forwarding a system prompt ChatMessageContent into the assistant thread; reusing a chat-history that contains SYSTEM entries; passing allowed_message_roles=[AuthorRole.USER] and then submitting an assistant-authored message; empty allowed_message_roles=[] which disallows everything except TOOL.

Related errors


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