deepset-ai/haystack · error · ValueError

Assistant message must contain one text part at most.

Error message

Assistant message must contain one text part at most.

What it means

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.

Source

Thrown at haystack/utils/jinja2_chat_extension.py:381

                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,
            )

        if role == "tool":
            tool_call_results = [part for part in parts if isinstance(part, ToolCallResult)]
            if len(tool_call_results) == 0 or len(tool_call_results) > 1 or len(parts) > len(tool_call_results):
                raise ValueError("Tool message must contain only one tool call result.")

            tool_result = tool_call_results[0].result

View on GitHub (pinned to e318778c9b)

Solutions

  1. Merge the text into a single part: join strings before rendering or use {{ text_a ~ text_b }}.
  2. Insert only one text ChatMessage into the assistant block, or pre-merge messages.
  3. Split into multiple assistant ChatMessages if multiple texts are genuinely needed.
  4. Move extra text into tool call arguments or a following user/system message as appropriate.

Example fix

// before
{% insert %}{{ assistant_msg }}{% endinsert %}Extra text
// after
{{ assistant_msg.text ~ "\nExtra text" }}  // single text part
Defensive patterns

Strategy: validation

Validate before calling

from haystack.dataclasses import ChatMessage, TextContent

def validate_assistant_text_count(msg: ChatMessage):
    texts = [p for p in msg.content_parts if isinstance(p, TextContent)]
    if len(texts) > 1:
        raise TypeError("Assistant message has more than one text part")

Type guard

def has_at_most_one_text(msg: ChatMessage) -> bool:
    return sum(isinstance(p, TextContent) for p in msg.content_parts) <= 1

Try / catch

try:
    messages = renderer.run(template=tpl, variables=vars)["messages"]
except ValueError as e:
    if "one text part at most" in str(e):
        log.error("Assistant block produced multiple text parts; merge before rendering")
    raise

Prevention

When it happens

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

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

Related errors


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