{"record":{"id":"19748efbba4e559f","repo":"microsoft/semantic-kernel","slug":"unsupported-role-in-chat-history-curr-message-ro","errorCode":null,"errorMessage":"Unsupported role in chat history: {curr_message.role}","messagePattern":"Unsupported role in chat history: (.+?)","errorType":"exception","errorClass":"ServiceInvalidRequestError","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py","lineNumber":247,"sourceCode":"                    # Under no circumstances should a tool message be the first message in the chat history\n                    raise ServiceInvalidRequestError(\"Tool message found without a preceding message.\")\n                if prev_message.role == AuthorRole.USER or prev_message.role == AuthorRole.SYSTEM:\n                    # A tool message should not be found after a user or system message\n                    # Please NOTE that in SK there are the USER role and the TOOL role, but in Anthropic\n                    # the tool messages are considered as USER messages. We are checking against the SK roles.\n                    raise ServiceInvalidRequestError(\"Tool message found after a user or system message.\")\n\n                formatted_message = MESSAGE_CONVERTERS[curr_message.role](curr_message)\n                if prev_message.role == AuthorRole.ASSISTANT:\n                    # The first tool message after an assistant message should be a new message\n                    formatted_messages.append(formatted_message)\n                else:\n                    # Append the tool message to the previous tool message.\n                    # This indicates that the assistant message requested multiple parallel tool calls.\n                    # Anthropic requires that parallel Tool messages are grouped together in a single message.\n                    formatted_messages[-1][content_key] += formatted_message[content_key]\n            else:\n                raise ServiceInvalidRequestError(f\"Unsupported role in chat history: {curr_message.role}\")\n\n        if system_message_count > 1:\n            logger.warning(\n                \"Anthropic service only supports one system message, but %s system messages were found.\"\n                \" Only the first system message will be included in the request.\",\n                system_message_count,\n            )\n\n        return formatted_messages, system_message_content\n\n    # endregion\n\n    def _create_chat_message_content(\n        self, response: Message, response_metadata: dict[str, Any]\n    ) -> \"ChatMessageContent\":\n        \"\"\"Create a chat message content object.\"\"\"\n        items: list[CMC_ITEM_TYPES] = []\n        items += self._get_tool_calls_from_message(response)","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py#L229-L265","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nhistory.add_message(ChatMessageContent(role=AuthorRole('annotator'), content=\"...\"))\n\n// after\n# keep annotations out of the model-facing history\nhistory.add_user_message(\"...\")","handlingStrategy":"validation","validationCode":"from semantic_kernel.contents import AuthorRole\n\nSUPPORTED = {AuthorRole.SYSTEM, AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL}\nunsupported = [i for i, m in enumerate(history) if m.role not in SUPPORTED]\nassert not unsupported, f\"Unsupported roles at indices: {unsupported}\"","typeGuard":"from semantic_kernel.contents import AuthorRole\n\ndef is_supported_role(role) -> bool:\n    return role in {AuthorRole.SYSTEM, AuthorRole.USER, AuthorRole.ASSISTANT, AuthorRole.TOOL}","tryCatchPattern":"from semantic_kernel.exceptions import ServiceInvalidRequestError\ntry:\n    await service.get_chat_message_contents(history=history, settings=settings)\nexcept ServiceInvalidRequestError as e:\n    if \"Unsupported role\" in str(e):\n        history = [m for m in history if m.role in SUPPORTED]  # strip annotations","preventionTips":["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."],"tags":["anthropic","chat-history","roles","validation","python"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}