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
Same role guard as the content-generation helper, applied inside AssistantThreadActions when creating a thread message. allowed_message_roles defaults to [USER, ASSISTANT]; TOOL is always allowed. Note a string message is auto-wrapped as USER, and messages containing FunctionCallContent are short-circuited (return None) before this check. Any remaining message with a disallowed role raises AgentExecutionException.
Source
Thrown at python/semantic_kernel/agents/open_ai/assistant_thread_actions.py:119
Providing an empty list will disallow all message roles.
kwargs: Additional keyword arguments.
Returns:
The created message.
"""
from semantic_kernel.contents.chat_message_content import ChatMessageContent
if isinstance(message, str):
message = ChatMessageContent(role=AuthorRole.USER, content=message)
if any(isinstance(item, FunctionCallContent) for item in message.items):
return None
# 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
**kwargs,
)
# endregion
# region Invocation Methods
@classmethod
async def invoke(View on GitHub (pinned to c028a0c7dc)
Solutions
- Pre-filter history to USER/ASSISTANT roles before passing to the assistant invoke path.
- Do not pass allowed_message_roles unless you actually need to restrict; the default covers the common case.
- Keep SYSTEM content in the assistant instructions, not in thread messages.
Example fix
// before allowed = [AuthorRole.USER] await actions.create_message(client, thread_id, assistant_msg, allowed_message_roles=allowed) # assistant_msg.role == ASSISTANT -> Invalid message role // after await actions.create_message(client, thread_id, assistant_msg) # default allows USER+ASSISTANT
Defensive patterns
Strategy: validation
Validate before calling
from semantic_kernel.contents import AuthorRole
def filter_thread_messages(history):
allowed = {AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL}
return [m for m in history if m.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
- Strip SYSTEM messages from history before submitting to a thread.
- Rely on the default allowed_message_roles rather than overriding it.
- Keep system text in the assistant definition.
When it happens
Trigger: Submitting a ChatMessageContent with role SYSTEM (or another disallowed role) to the thread-actions message-create path; passing a custom allowed_message_roles list that excludes the role of a message being added.
Common situations: Reusing a multi-role chat history (with SYSTEM entries) as the thread's messages; building allowed_message_roles dynamically and accidentally omitting the needed role; migrating prompts that previously put system text inline.
Related errors
- Invalid message role `{message.role.value}`. Allowed roles a
- FunctionChoiceBehavior with type '{function_choice_behavior.
- FunctionChoiceBehavior.Auto(auto_invoke=False) is not suppor
- FunctionChoiceBehavior filters must not be empty. Provide at
- Unknown filter key(s): {sorted(unknown_keys)}. Valid filter
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/dce64efde846b5ad.
Report an issue: GitHub.