zylon-ai/private-gpt · error · ValueError
Chat history contains a tool message: {message.content}. Rea
Error message
Chat history contains a tool message: {message.content}. Reason: {reason or 'Unknown'} What it means
Raised by _assert_repair_without_tools (used by repair_without_tools) after repair: the history must be free of tool artifacts. A message counts as a tool artifact if role=='tool', if additional_kwargs contains 'tool_calls', or if it contains 'tool_call_id'. Any hit means tool content survived into a tool-free pipeline.
Source
Thrown at private_gpt/components/chat/processors/chat_history/memory/utils/repairs.py:189
This is used to ensure that the chat history is
clean and does not contain any tool calls or tool messages.
"""
if not chat_history:
return
def is_a_potential_tool_message(msg: ChatMessage) -> tuple[bool, str | None]:
if msg.role == "tool":
return True, "Tool message detected"
if msg.additional_kwargs.get("tool_calls", None):
return True, "Tool call detected"
if msg.additional_kwargs.get("tool_call_id", None):
return True, "Tool call ID detected"
return False, None
for message in chat_history:
is_tool, reason = is_a_potential_tool_message(message)
if is_tool:
raise ValueError(
f"Chat history contains a tool message: {message.content}. Reason: {reason or 'Unknown'}"
)
def _assert_only_one_user_block(
chat_history: list[ChatMessage],
) -> None:
"""Assert that the chat history contains exactly one user block.
This is used to ensure that the chat history
is clean and contains only one user block.
"""
if not chat_history:
return
matrix = get_user_blocks(chat_history)
if not matrix:
raise ValueError("Chat history does not contain any user messages.")View on GitHub (pinned to 4a030776a3)
Solutions
- Use repair_with_tools() instead when the history legitimately contains tool flows
- Strip 'tool_calls' and 'tool_call_id' from additional_kwargs and drop role=='tool' messages before calling repair_without_tools()
- On the producer side, don't persist tool metadata when the target path is tool-free
Example fix
# before
repaired = repair_without_tools(chat_history)
# after
scrubbed = [
m for m in chat_history if m.role != "tool"
]
for m in scrubbed:
m.additional_kwargs.pop("tool_calls", None)
m.additional_kwargs.pop("tool_call_id", None)
repaired = repair_without_tools(scrubbed) Defensive patterns
Strategy: validation
Validate before calling
def is_tool_free(history: list[ChatMessage]) -> bool:
return not any(
m.role == "tool"
or m.additional_kwargs.get("tool_calls")
or m.additional_kwargs.get("tool_call_id")
for m in history
)
assert is_tool_free(conversation_messages) Type guard
def is_tool_free(history: list[ChatMessage]) -> bool:
return not any(
m.role == "tool"
or m.additional_kwargs.get("tool_calls")
or m.additional_kwargs.get("tool_call_id")
for m in history
) Try / catch
try:
repaired = repair_without_tools(chat_history)
except ValueError as e:
if "contains a tool message" in str(e):
repaired = repair_with_tools(chat_history) # correct path for tool histories
else:
raise Prevention
- Pick repair_with_tools vs repair_without_tools based on whether tools were used in the session
- Scrub tool_calls/tool_call_id kwargs when exporting to tool-free pipelines
- Keep tool metadata namespaced so accidental collisions can't occur
When it happens
Trigger: Calling repair_without_tools() on history that includes tool results or assistant messages carrying tool_calls/tool_call_id in additional_kwargs — for example feeding an agent-with-tools transcript into a no-tools model path.
Common situations: Switching a conversation from a tool-enabled agent to a plain chat model; persisted assistant messages retaining tool_calls metadata; mixing RAG 'sources' kwargs that accidentally use the tool_calls key.
Related errors
- Tool message {j} in user block {i} does not follow an assist
- User block {i} is empty.
- User block {i} does not start with a user message: {block[0]
- User block {i} does not end with an assistant message: {bloc
- User block {i} does not end with an assistant or tool messag
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/47c8abdf4717f765.
Report an issue: GitHub.