microsoft/semantic-kernel · error · ServiceInvalidRequestError

Tool message found without a preceding message.

Error message

Tool message found without a preceding message.

What it means

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.

Source

Thrown at python/semantic_kernel/connectors/ai/anthropic/services/anthropic_chat_completion.py:230

            A tuple containing the prepared chat history and the first SYSTEM message content.
        """
        system_message_content = None
        system_message_count = 0
        formatted_messages: list[dict[str, Any]] = []
        for i in range(len(chat_history)):
            prev_message = chat_history[i - 1] if i > 0 else None
            curr_message = chat_history[i]
            if curr_message.role == AuthorRole.SYSTEM:
                # Skip system messages after the first one is found
                if system_message_count == 0:
                    system_message_content = curr_message.content
                system_message_count += 1
            elif curr_message.role == AuthorRole.USER or curr_message.role == AuthorRole.ASSISTANT:
                formatted_messages.append(MESSAGE_CONVERTERS[curr_message.role](curr_message))
            elif curr_message.role == AuthorRole.TOOL:
                if prev_message is None:
                    # 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}")

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. 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.
  2. If reconstructing a function-calling conversation, start from the original user prompt and append messages in order: user → assistant(tool_use) → tool(result).
  3. Validate the history ordering with a helper before calling the service (see defense validationCode).

Example fix

// before
history = ChatHistory()
history.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[FunctionResultContent(...)]))
await service.get_chat_message_contents(history=history, settings=settings)

// after
history = ChatHistory()
history.add_user_message("Please run the tool.")
history.add_message(assistant_msg_with_tool_call)
history.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[FunctionResultContent(...)]))
await service.get_chat_message_contents(history=history, settings=settings)
Defensive patterns

Strategy: validation

Validate before calling

from semantic_kernel.contents import AuthorRole

def has_tool_first(history) -> bool:
    return len(history) > 0 and history[0].role == AuthorRole.TOOL

# before calling the service:
assert not has_tool_first(history), "A TOOL message cannot be the first message in chat_history."

Type guard

from semantic_kernel.contents import AuthorRole

def history_starts_with_valid_role(history) -> bool:
    if not history:
        return True
    return history[0].role in (AuthorRole.SYSTEM, AuthorRole.USER)

Try / catch

from semantic_kernel.exceptions import ServiceInvalidRequestError
try:
    await service.get_chat_message_contents(history=history, settings=settings)
except ServiceInvalidRequestError as e:
    if "without a preceding message" in str(e):
        history.insert_message(0, ChatMessageContent(role=AuthorRole.USER, content="..."))

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/d1da7226ce9db4ed. Report an issue: GitHub.