microsoft/semantic-kernel · error · ServiceInvalidRequestError
Unsupported role in chat history: {curr_message.role}
Error message
Unsupported role in chat history: {curr_message.role} What it means
Raised by the Anthropic connector when a chat_history message has an AuthorRole other than SYSTEM, USER, ASSISTANT, or TOOL. The Anthropic Messages API only supports those four roles (with tool results folded into user turns), so any custom or unexpected role cannot be mapped to a valid request message.
Source
Thrown at python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py:247
# Under no circumstances should a tool message be the first message in the chat history
raise ServiceInvalidRequestError("Tool message found without a preceding message.")
if prev_message.role == AuthorRole.USER or prev_message.role == AuthorRole.SYSTEM:
# A tool message should not be found after a user or system message
# Please NOTE that in SK there are the USER role and the TOOL role, but in Anthropic
# the tool messages are considered as USER messages. We are checking against the SK roles.
raise ServiceInvalidRequestError("Tool message found after a user or system message.")
formatted_message = MESSAGE_CONVERTERS[curr_message.role](curr_message)
if prev_message.role == AuthorRole.ASSISTANT:
# The first tool message after an assistant message should be a new message
formatted_messages.append(formatted_message)
else:
# Append the tool message to the previous tool message.
# This indicates that the assistant message requested multiple parallel tool calls.
# Anthropic requires that parallel Tool messages are grouped together in a single message.
formatted_messages[-1][content_key] += formatted_message[content_key]
else:
raise ServiceInvalidRequestError(f"Unsupported role in chat history: {curr_message.role}")
if system_message_count > 1:
logger.warning(
"Anthropic service only supports one system message, but %s system messages were found."
" Only the first system message will be included in the request.",
system_message_count,
)
return formatted_messages, system_message_content
# endregion
def _create_chat_message_content(
self, response: Message, response_metadata: dict[str, Any]
) -> "ChatMessageContent":
"""Create a chat message content object."""
items: list[CMC_ITEM_TYPES] = []
items += self._get_tool_calls_from_message(response)View on GitHub (pinned to c028a0c7dc)
Solutions
- Filter out or remap non-standard roles before calling the service — convert annotations to USER messages or store them out-of-band.
- Ensure every ChatMessageContent.role is one of AuthorRole.SYSTEM, USER, ASSISTANT, or TOOL.
- Add a pre-flight filter that drops or warns on unsupported roles (see defense validationCode).
Example fix
// before
history.add_message(ChatMessageContent(role=AuthorRole('annotator'), content="..."))
// after
# keep annotations out of the model-facing history
history.add_user_message("...") Defensive patterns
Strategy: validation
Validate before calling
from semantic_kernel.contents import AuthorRole
SUPPORTED = {AuthorRole.SYSTEM, AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL}
unsupported = [i for i, m in enumerate(history) if m.role not in SUPPORTED]
assert not unsupported, f"Unsupported roles at indices: {unsupported}" Type guard
from semantic_kernel.contents import AuthorRole
def is_supported_role(role) -> bool:
return role in {AuthorRole.SYSTEM, AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL} Try / catch
from semantic_kernel.exceptions import ServiceInvalidRequestError
try:
await service.get_chat_message_contents(history=history, settings=settings)
except ServiceInvalidRequestError as e:
if "Unsupported role" in str(e):
history = [m for m in history if m.role in SUPPORTED] # strip annotations Prevention
- Keep annotation/logging metadata out of the model-facing ChatHistory.
- Validate roles before each service call in middleware.
- Map any custom role to USER if it must reach the model.
When it happens
Trigger: A ChatMessageContent whose role is a custom/named role (e.g. AuthorRole('notes'), 'developer', or any string not in the four supported enums) reaches the formatting loop. Also triggered by accidentally setting role to None or a non-AuthorRole value.
Common situations: Using custom roles for annotations or logging that leak into the history passed to the service; upgrading SK versions where new roles were introduced; programmatically assigning roles from external config without validation.
Related errors
- Tool message found without a preceding message.
- Tool message found after a user or system message.
- No FunctionResultContent found in the message items
- Chat history can't contain only system messages.
- Chat history must contain at least one message
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/19748efbba4e559f.
Report an issue: GitHub.