microsoft/semantic-kernel · error · ServiceInvalidRequestError

Tool message found after a user or system message.

Error message

Tool message found after a user or system message.

What it means

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.

Source

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

        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}")

        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,

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Insert the assistant message containing the tool_use (FunctionCallContent) immediately before the TOOL message so the order is ...assistant(tool_use) → tool(result).
  2. Remove any intervening USER/SYSTEM messages between the assistant tool_use and the tool result.
  3. Use a pre-flight ordering check (see defense validationCode) to detect a TOOL message whose predecessor is not ASSISTANT or TOOL.

Example fix

// before
history.add_user_message("Use the tool.")
history.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[result]))  # prev is USER → error

// after
history.add_user_message("Use the tool.")
history.add_message(assistant_with_function_call_content)
history.add_message(ChatMessageContent(role=AuthorRole.TOOL, items=[result]))  # prev is ASSISTANT → ok
Defensive patterns

Strategy: validation

Validate before calling

from semantic_kernel.contents import AuthorRole

def tool_after_user_or_system(history) -> bool:
    for i, msg in enumerate(history):
        if msg.role == AuthorRole.TOOL and i > 0:
            if history[i - 1].role in (AuthorRole.USER, AuthorRole.SYSTEM):
                return True
    return False

assert not tool_after_user_or_system(history), "TOOL message must follow an ASSISTANT (or TOOL) message."

Type guard

from semantic_kernel.contents import AuthorRole

def tool_preceded_by_assistant_or_tool(history, i) -> bool:
    if history[i].role != AuthorRole.TOOL:
        return True
    if i == 0:
        return False
    return history[i - 1].role in (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 "after a user or system message" in str(e):
        # insert the missing assistant tool_use turn before the tool result
        ...

Prevention

When it happens

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

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

Related errors


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