{"record":{"id":"d1da7226ce9db4ed","repo":"microsoft/semantic-kernel","slug":"tool-message-found-without-a-preceding-message","errorCode":null,"errorMessage":"Tool message found without a preceding message.","messagePattern":"Tool message found without a preceding message\\.","errorType":"exception","errorClass":"ServiceInvalidRequestError","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py","lineNumber":230,"sourceCode":"            A tuple containing the prepared chat history and the first SYSTEM message content.\n        \"\"\"\n        system_message_content = None\n        system_message_count = 0\n        formatted_messages: list[dict[str, Any]] = []\n        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","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py#L212-L248","documentation":"Raised by the Anthropic chat-completion connector when the first entry in the supplied chat_history has the TOOL role. Anthropic's Messages API models tool results as user-turn content that must follow an assistant turn containing a tool_use block, so a tool message with no predecessor is structurally invalid and cannot be serialized into a valid request.","triggerScenarios":"Calling AnthropicChatCompletion.get_chat_message_contents / get_streaming_chat_message_contents with a ChatHistory whose index 0 element has role=AuthorRole.TOOL. This happens when FunctionResultContent is appended before any user/assistant message, or when a chat history is sliced/filtered and the leading context (user or assistant turn) is dropped.","commonSituations":"Manually building a ChatHistory and adding a tool result first; reusing a history fragment that starts at a tool-call result after trimming earlier turns; function-calling loops where only the tool result is retained between turns.","solutions":["Ensure chat_history[0] is a USER or SYSTEM message before any TOOL message — prepend a user message or restore the trimmed assistant/user context.","If reconstructing a function-calling conversation, start from the original user prompt and append messages in order: user → assistant(tool_use) → tool(result).","Validate the history ordering with a helper before calling the service (see defense validationCode)."],"exampleFix":"// before\nhistory = ChatHistory()\nhistory.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[FunctionResultContent(...)]))\nawait service.get_chat_message_contents(history=history, settings=settings)\n\n// after\nhistory = ChatHistory()\nhistory.add_user_message(\"Please run the tool.\")\nhistory.add_message(assistant_msg_with_tool_call)\nhistory.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[FunctionResultContent(...)]))\nawait service.get_chat_message_contents(history=history, settings=settings)","handlingStrategy":"validation","validationCode":"from semantic_kernel.contents import AuthorRole\n\ndef has_tool_first(history) -> bool:\n    return len(history) > 0 and history[0].role == AuthorRole.TOOL\n\n# before calling the service:\nassert not has_tool_first(history), \"A TOOL message cannot be the first message in chat_history.\"","typeGuard":"from semantic_kernel.contents import AuthorRole\n\ndef history_starts_with_valid_role(history) -> bool:\n    if not history:\n        return True\n    return history[0].role in (AuthorRole.SYSTEM, AuthorRole.USER)","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 \"without a preceding message\" in str(e):\n        history.insert_message(0, ChatMessageContent(role=AuthorRole.USER, content=\"...\"))","preventionTips":["Always seed ChatHistory with a USER or SYSTEM message first.","When slicing histories for retries, keep the leading user/assistant context.","In function-calling loops, retain the full user→assistant→tool ordering."],"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"}