{"record":{"id":"da1088427a1854a3","repo":"deepset-ai/haystack","slug":"assistant-message-must-contain-one-text-part-at-mo","errorCode":null,"errorMessage":"Assistant message must contain one text part at most.","messagePattern":"Assistant message must contain one text part at most\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/utils/jinja2_chat_extension.py","lineNumber":381,"sourceCode":"                raise ValueError(\n                    \"User message must contain only TextContent, string, ImageContent or FileContent parts.\"\n                )\n            return ChatMessage.from_user(meta=meta, name=name, content_parts=valid_parts)\n\n        if role == \"system\":\n            if not isinstance(parts[0], TextContent):\n                raise ValueError(\"System message must contain a text part.\")\n            text = parts[0].text\n            if len(parts) > 1:\n                raise ValueError(\"System message must contain only one text part.\")\n            return ChatMessage.from_system(meta=meta, name=name, text=text)\n\n        if role == \"assistant\":\n            texts = [part.text for part in parts if isinstance(part, TextContent)]\n            tool_calls = [part for part in parts if isinstance(part, ToolCall)]\n            reasoning = [part for part in parts if isinstance(part, ReasoningContent)]\n            if len(texts) > 1:\n                raise ValueError(\"Assistant message must contain one text part at most.\")\n            if len(texts) == 0 and len(tool_calls) == 0:\n                raise ValueError(\"Assistant message must contain at least one text or tool call part.\")\n            if len(parts) > len(texts) + len(tool_calls) + len(reasoning):\n                raise ValueError(\"Assistant message must contain only text, tool call or reasoning parts.\")\n            return ChatMessage.from_assistant(\n                meta=meta,\n                name=name,\n                text=texts[0] if texts else None,\n                tool_calls=tool_calls or None,\n                reasoning=reasoning[0] if reasoning else None,\n            )\n\n        if role == \"tool\":\n            tool_call_results = [part for part in parts if isinstance(part, ToolCallResult)]\n            if len(tool_call_results) == 0 or len(tool_call_results) > 1 or len(parts) > len(tool_call_results):\n                raise ValueError(\"Tool message must contain only one tool call result.\")\n\n            tool_result = tool_call_results[0].result","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/utils/jinja2_chat_extension.py#L363-L399","documentation":"Assistant messages built from a template may include at most one TextContent part (alongside tool calls and reasoning). This error means two or more text parts were produced for one assistant message.","triggerScenarios":"An assistant block containing multiple text segments and/or multiple {% insert %} calls each yielding a text part; inserting a list of assistant messages into a single assistant message block.","commonSituations":"Templates that emit answer text plus an inserted explanation message into the same assistant turn; loops inside assistant blocks; converting multi-part assistant outputs from other frameworks.","solutions":["Merge the text into a single part: join strings before rendering or use {{ text_a ~ text_b }}.","Insert only one text ChatMessage into the assistant block, or pre-merge messages.","Split into multiple assistant ChatMessages if multiple texts are genuinely needed.","Move extra text into tool call arguments or a following user/system message as appropriate."],"exampleFix":"// before\n{% insert %}{{ assistant_msg }}{% endinsert %}Extra text\n// after\n{{ assistant_msg.text ~ \"\\nExtra text\" }}  // single text part","handlingStrategy":"validation","validationCode":"from haystack.dataclasses import ChatMessage, TextContent\n\ndef validate_assistant_text_count(msg: ChatMessage):\n    texts = [p for p in msg.content_parts if isinstance(p, TextContent)]\n    if len(texts) > 1:\n        raise TypeError(\"Assistant message has more than one text part\")","typeGuard":"def has_at_most_one_text(msg: ChatMessage) -> bool:\n    return sum(isinstance(p, TextContent) for p in msg.content_parts) <= 1","tryCatchPattern":"try:\n    messages = renderer.run(template=tpl, variables=vars)[\"messages\"]\nexcept ValueError as e:\n    if \"one text part at most\" in str(e):\n        log.error(\"Assistant block produced multiple text parts; merge before rendering\")\n    raise","preventionTips":["Join multiple assistant texts into one string before rendering.","Insert at most one text ChatMessage per assistant block.","Avoid emitting text both directly and via {% insert %} in the same block.","Test templates that combine static text with inserted assistant messages."],"tags":["chat-template","message-validation","assistant-message"],"backgroundTag":"invalid-message-content-part","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}