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
- 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.
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
- 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.
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
- Tool message found without a preceding message.
- Unsupported role in chat history: {curr_message.role}
- No FunctionResultContent found in the message items
- Auto invocation of tool calls may only be used with a single
- No function results were returned.
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/fd3895763c70b906.
Report an issue: GitHub.