{"record":{"id":"e478b6768707fe83","repo":"deepset-ai/haystack","slug":"for-compatibility-with-the-hugging-face-api-a-ch","errorCode":null,"errorMessage":"For compatibility with the Hugging Face API, a `ChatMessage` with a `ToolCallResult` cannot contain any other content.","messagePattern":"For compatibility with the Hugging Face API, a `ChatMessage` with a `ToolCallResult` cannot contain any other content\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/utils/hf.py","lineNumber":77,"sourceCode":"    (which follows the OpenAI-compatible chat completion format) does not support reasoning\n    in input messages. Reasoning is captured from model outputs for transparency but is not\n    sent back to the API in multi-turn conversations.\n    \"\"\"\n    text_contents = message.texts\n    tool_calls = message.tool_calls\n    tool_call_results = message.tool_call_results\n    images = message.images\n\n    # Filter out ReasoningContent from the content list for validation\n    # ReasoningContent is for human transparency only, not sent to the API\n    non_reasoning_content = [c for c in message._content if not isinstance(c, ReasoningContent)]\n\n    if not text_contents and not tool_calls and not tool_call_results and not images:\n        raise ValueError(\n            \"A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, `ToolCallResult`, or `ImageContent`.\"\n        )\n    if len(tool_call_results) > 0 and len(non_reasoning_content) > 1:\n        raise ValueError(\n            \"For compatibility with the Hugging Face API, a `ChatMessage` with a `ToolCallResult` \"\n            \"cannot contain any other content.\"\n        )\n\n    # HF always expects a content field, even if it is empty\n    hf_msg: dict[str, Any] = {\"role\": message._role.value, \"content\": \"\"}\n\n    if tool_call_results:\n        result = tool_call_results[0]\n        hf_msg[\"content\"] = result.result\n        if tc_id := result.origin.id:\n            hf_msg[\"tool_call_id\"] = tc_id\n        # HF does not provide a way to communicate errors in tool invocations, so we ignore the error field\n        return hf_msg\n\n    # Handle multimodal content (text + images) preserving order\n    if text_contents or images:\n        content_parts: list[dict[str, Any]] = []","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/utils/hf.py#L59-L95","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nmsg = ChatMessage(_role=\"tool\", _content=[ToolCallResult(...), TextContent(\"done\")])\n// after\nresult_msg = ChatMessage(_role=\"tool\", _content=[ToolCallResult(...)])\ntext_msg = ChatMessage.from_assistant(\"done\")","handlingStrategy":"validation","validationCode":"def is_tool_result_only(msg) -> bool:\n    from haystack.dataclasses import ToolCallResult\n    results = [c for c in msg._content if isinstance(c, ToolCallResult)]\n    return len(results) == 0 or len(msg._content) == len(results)","typeGuard":"def can_convert_with_tool_result(msg) -> bool:\n    from haystack.dataclasses import ToolCallResult\n    has_result = any(isinstance(c, ToolCallResult) for c in msg._content)\n    return not has_result or len(msg._content) == 1","tryCatchPattern":"try:\n    hf_msg = convert_message_to_hf_format(msg)\nexcept ValueError:\n    # split: emit result-only message, handle other parts separately\n    hf_msg = convert_message_to_hf_format(result_only_message(msg))","preventionTips":["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"],"tags":["haystack","hugging-face","chatmessage","valueerror","tool-call-result"],"backgroundTag":"invalid-message-composition","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}