{"record":{"id":"fd3895763c70b906","repo":"microsoft/semantic-kernel","slug":"tool-message-found-after-a-user-or-system-message","errorCode":null,"errorMessage":"Tool message found after a user or system message.","messagePattern":"Tool message found after a user or system message\\.","errorType":"exception","errorClass":"ServiceInvalidRequestError","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py","lineNumber":235,"sourceCode":"        for i in range(len(chat_history)):\n            prev_message = chat_history[i - 1] if i > 0 else None\n            curr_message = chat_history[i]\n            if curr_message.role == AuthorRole.SYSTEM:\n                # Skip system messages after the first one is found\n                if system_message_count == 0:\n                    system_message_content = curr_message.content\n                system_message_count += 1\n            elif curr_message.role == AuthorRole.USER or curr_message.role == AuthorRole.ASSISTANT:\n                formatted_messages.append(MESSAGE_CONVERTERS[curr_message.role](curr_message))\n            elif curr_message.role == AuthorRole.TOOL:\n                if prev_message is None:\n                    # 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,","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py#L217-L253","documentation":"Raised by the Anthropic connector when a TOOL message is immediately preceded by a USER or SYSTEM message. Anthropic requires tool_result blocks to sit inside a user turn that directly follows an assistant turn containing the matching tool_use; a tool result after a plain user/system turn has no tool_use to answer and is rejected before the request is sent.","triggerScenarios":"ChatHistory ordering where a TOOL message comes right after a USER or SYSTEM message (e.g. user → tool with no intervening assistant tool_use). Also triggered by interleaving plain user turns between an assistant tool_use and the corresponding tool result.","commonSituations":"Appending a FunctionResultContent after a fresh user message instead of after the assistant's tool-call turn; logging/echo middleware that inserts user messages between the assistant call and the result; replaying histories where the assistant turn was lost.","solutions":["Insert the assistant message containing the tool_use (FunctionCallContent) immediately before the TOOL message so the order is ...assistant(tool_use) → tool(result).","Remove any intervening USER/SYSTEM messages between the assistant tool_use and the tool result.","Use a pre-flight ordering check (see defense validationCode) to detect a TOOL message whose predecessor is not ASSISTANT or TOOL."],"exampleFix":"// before\nhistory.add_user_message(\"Use the tool.\")\nhistory.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[result]))  # prev is USER → error\n\n// after\nhistory.add_user_message(\"Use the tool.\")\nhistory.add_message(assistant_with_function_call_content)\nhistory.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[result]))  # prev is ASSISTANT → ok","handlingStrategy":"validation","validationCode":"from semantic_kernel.contents import AuthorRole\n\ndef tool_after_user_or_system(history) -> bool:\n    for i, msg in enumerate(history):\n        if msg.role == AuthorRole.TOOL and i > 0:\n            if history[i - 1].role in (AuthorRole.USER, AuthorRole.SYSTEM):\n                return True\n    return False\n\nassert not tool_after_user_or_system(history), \"TOOL message must follow an ASSISTANT (or TOOL) message.\"","typeGuard":"from semantic_kernel.contents import AuthorRole\n\ndef tool_preceded_by_assistant_or_tool(history, i) -> bool:\n    if history[i].role != AuthorRole.TOOL:\n        return True\n    if i == 0:\n        return False\n    return history[i - 1].role in (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 \"after a user or system message\" in str(e):\n        # insert the missing assistant tool_use turn before the tool result\n        ...","preventionTips":["Keep each TOOL message directly after the ASSISTANT message that issued the tool_use.","Avoid inserting plain USER/SYSTEM turns between a tool_use and its result.","Use SK's auto-invocation middleware to manage ordering automatically."],"tags":["anthropic","chat-history","function-calling","validation","python"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}