deepset-ai/haystack · error · ValueError
For compatibility with the Hugging Face API, a `ChatMessage`
Error message
For compatibility with the Hugging Face API, a `ChatMessage` with a `ToolCallResult` cannot contain any other content.
What it means
ValueError raised in `convert_message_to_hf_format` when a ChatMessage contains a ToolCallResult together with any other non-reasoning content. The Hugging Face chat API requires a message that carries a tool result to contain only that result, so mixing it with text, tool calls, or images is rejected.
Source
Thrown at haystack/utils/hf.py:77
(which follows the OpenAI-compatible chat completion format) does not support reasoning
in input messages. Reasoning is captured from model outputs for transparency but is not
sent back to the API in multi-turn conversations.
"""
text_contents = message.texts
tool_calls = message.tool_calls
tool_call_results = message.tool_call_results
images = message.images
# Filter out ReasoningContent from the content list for validation
# ReasoningContent is for human transparency only, not sent to the API
non_reasoning_content = [c for c in message._content if not isinstance(c, ReasoningContent)]
if not text_contents and not tool_calls and not tool_call_results and not images:
raise ValueError(
"A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, `ToolCallResult`, or `ImageContent`."
)
if len(tool_call_results) > 0 and len(non_reasoning_content) > 1:
raise ValueError(
"For compatibility with the Hugging Face API, a `ChatMessage` with a `ToolCallResult` "
"cannot contain any other content."
)
# HF always expects a content field, even if it is empty
hf_msg: dict[str, Any] = {"role": message._role.value, "content": ""}
if tool_call_results:
result = tool_call_results[0]
hf_msg["content"] = result.result
if tc_id := result.origin.id:
hf_msg["tool_call_id"] = tc_id
# HF does not provide a way to communicate errors in tool invocations, so we ignore the error field
return hf_msg
# Handle multimodal content (text + images) preserving order
if text_contents or images:
content_parts: list[dict[str, Any]] = []View on GitHub (pinned to e318778c9b)
Solutions
- Split the message: put the ToolCallResult in its own ChatMessage and move other content into a separate message
- Strip other content parts before conversion when only the tool result should be sent
- Use ChatMessage.from_tool_call_result (or the appropriate constructor) so only the result is in the content list
Example fix
// before
msg = ChatMessage(_role="tool", _content=[ToolCallResult(...), TextContent("done")])
// after
result_msg = ChatMessage(_role="tool", _content=[ToolCallResult(...)])
text_msg = ChatMessage.from_assistant("done") Defensive patterns
Strategy: validation
Validate before calling
def is_tool_result_only(msg) -> bool:
from haystack.dataclasses import ToolCallResult
results = [c for c in msg._content if isinstance(c, ToolCallResult)]
return len(results) == 0 or len(msg._content) == len(results) Type guard
def can_convert_with_tool_result(msg) -> bool:
from haystack.dataclasses import ToolCallResult
has_result = any(isinstance(c, ToolCallResult) for c in msg._content)
return not has_result or len(msg._content) == 1 Try / catch
try:
hf_msg = convert_message_to_hf_format(msg)
except ValueError:
# split: emit result-only message, handle other parts separately
hf_msg = convert_message_to_hf_format(result_only_message(msg)) Prevention
- Keep ToolCallResult in its own ChatMessage, never mixed with text or tool calls
- Build tool-result messages with the dedicated from_tool_call_result-style constructor
- Review message-merging logic that appends content to existing messages
When it happens
Trigger: Calling convert_message_to_hf_format with a message whose content list has a ToolCallResult plus, e.g., a TextContent or ToolCall (len(tool_call_results) > 0 and len(non_reasoning_content) > 1), which typically happens when merging tool output and explanation into a single message.
Common situations: Aggregating a tool's result and assistant commentary into one ChatMessage; frameworks that append ToolCallResult to an existing message instead of creating a separate one; multi-tool pipelines that batch results.
Related errors
- A `ChatMessage` must contain at least one `TextContent`, `To
- Message template produced content that couldn't be parsed in
- Component named {receiver_component_name} not found in the p
- Component named {name} not found in the pipeline.
- Component named '${component_name}' not found in the pipelin
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/e478b6768707fe83.
Report an issue: GitHub.