{"record":{"id":"7a95909c85d8728a","repo":"deepset-ai/haystack","slug":"system-message-must-contain-only-one-text-part","errorCode":null,"errorMessage":"System message must contain only one text part.","messagePattern":"System message must contain only one text part\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/utils/jinja2_chat_extension.py","lineNumber":373,"sourceCode":"\n        :raises ValueError: If content parts don't allow to build a valid ChatMessage object or the role is not\n                            supported\n        \"\"\"\n\n        if role == \"user\":\n            valid_parts = [part for part in parts if isinstance(part, (TextContent, str, ImageContent, FileContent))]\n            if len(parts) != len(valid_parts):\n                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,","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/utils/jinja2_chat_extension.py#L355-L391","documentation":"System messages built from a chat template may contain exactly one text part and nothing else. This error indicates the system message accumulated multiple text parts, which Haystack does not allow.","triggerScenarios":"Multiple {% insert %} expressions or several text segments inside a system message block, each producing its own TextContent part; inserting a list of multiple messages into a system block.","commonSituations":"Concatenating multiple instruction messages into one system prompt via insert; a loop inside the system block emitting several text parts; merging system prompts from multiple components.","solutions":["Combine the text into a single part, e.g. concatenate strings in Jinja2: {{ a }} {{ b }}.","Insert only one ChatMessage into the system block, or merge messages before inserting.","If multiple system messages are needed, send them as separate system ChatMessages rather than parts.","Use message join logic outside the template to produce a single text string."],"exampleFix":"// before\n{% insert %}{{ msg_a }}{% endinsert %}{% insert %}{{ msg_b }}{% endinsert %}\n// after\n{{ msg_a.text }}\\n{{ msg_b.text }}  // single text part","handlingStrategy":"validation","validationCode":"from haystack.dataclasses import ChatMessage, TextContent\n\ndef validate_single_text_system(msg: ChatMessage):\n    texts = [p for p in msg.content_parts if isinstance(p, TextContent)]\n    if len(msg.content_parts) != 1 or len(texts) != 1:\n        raise TypeError(\"System message must be exactly one TextContent part\")","typeGuard":"def is_single_text(msg: ChatMessage) -> bool:\n    parts = msg.content_parts\n    return len(parts) == 1 and isinstance(parts[0], TextContent)","tryCatchPattern":"try:\n    messages = renderer.run(template=tpl, variables=vars)[\"messages\"]\nexcept ValueError as e:\n    if \"only one text part\" in str(e):\n        log.error(\"Multiple text parts in system message; merge texts before rendering\")\n    raise","preventionTips":["Concatenate text in Jinja2 ({{ a }} {{ b }}) instead of multiple inserts.","Merge multiple system prompts into one string before rendering.","Limit system blocks to a single text variable or single inserted message.","Avoid loops inside system message blocks."],"tags":["chat-template","message-validation","system-prompt"],"backgroundTag":"invalid-message-content-part","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}