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
- 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).
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
- 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.
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
- Tool message found after a user or system 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/d1da7226ce9db4ed.
Report an issue: GitHub.