deepset-ai/haystack · error · ValueError

System message must contain only one text part.

Error message

System message must contain only one text part.

What it means

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.

Source

Thrown at haystack/utils/jinja2_chat_extension.py:373

        :raises ValueError: If content parts don't allow to build a valid ChatMessage object or the role is not
                            supported
        """

        if role == "user":
            valid_parts = [part for part in parts if isinstance(part, (TextContent, str, ImageContent, FileContent))]
            if len(parts) != len(valid_parts):
                raise ValueError(
                    "User message must contain only TextContent, string, ImageContent or FileContent parts."
                )
            return ChatMessage.from_user(meta=meta, name=name, content_parts=valid_parts)

        if role == "system":
            if not isinstance(parts[0], TextContent):
                raise ValueError("System message must contain a text part.")
            text = parts[0].text
            if len(parts) > 1:
                raise ValueError("System message must contain only one text part.")
            return ChatMessage.from_system(meta=meta, name=name, text=text)

        if role == "assistant":
            texts = [part.text for part in parts if isinstance(part, TextContent)]
            tool_calls = [part for part in parts if isinstance(part, ToolCall)]
            reasoning = [part for part in parts if isinstance(part, ReasoningContent)]
            if len(texts) > 1:
                raise ValueError("Assistant message must contain one text part at most.")
            if len(texts) == 0 and len(tool_calls) == 0:
                raise ValueError("Assistant message must contain at least one text or tool call part.")
            if len(parts) > len(texts) + len(tool_calls) + len(reasoning):
                raise ValueError("Assistant message must contain only text, tool call or reasoning parts.")
            return ChatMessage.from_assistant(
                meta=meta,
                name=name,
                text=texts[0] if texts else None,
                tool_calls=tool_calls or None,
                reasoning=reasoning[0] if reasoning else None,

View on GitHub (pinned to e318778c9b)

Solutions

  1. Combine the text into a single part, e.g. concatenate strings in Jinja2: {{ a }} {{ b }}.
  2. Insert only one ChatMessage into the system block, or merge messages before inserting.
  3. If multiple system messages are needed, send them as separate system ChatMessages rather than parts.
  4. Use message join logic outside the template to produce a single text string.

Example fix

// before
{% insert %}{{ msg_a }}{% endinsert %}{% insert %}{{ msg_b }}{% endinsert %}
// after
{{ msg_a.text }}\n{{ msg_b.text }}  // single text part
Defensive patterns

Strategy: validation

Validate before calling

from haystack.dataclasses import ChatMessage, TextContent

def validate_single_text_system(msg: ChatMessage):
    texts = [p for p in msg.content_parts if isinstance(p, TextContent)]
    if len(msg.content_parts) != 1 or len(texts) != 1:
        raise TypeError("System message must be exactly one TextContent part")

Type guard

def is_single_text(msg: ChatMessage) -> bool:
    parts = msg.content_parts
    return len(parts) == 1 and isinstance(parts[0], TextContent)

Try / catch

try:
    messages = renderer.run(template=tpl, variables=vars)["messages"]
except ValueError as e:
    if "only one text part" in str(e):
        log.error("Multiple text parts in system message; merge texts before rendering")
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/7a95909c85d8728a. Report an issue: GitHub.