deepset-ai/haystack · error · ValueError
Assistant message must contain at least one text or tool cal
Error message
Assistant message must contain at least one text or tool call part.
What it means
An assistant message built from a template ended up with no text and no tool call parts (only reasoning or nothing at all), so there is no valid content to construct the assistant ChatMessage from.
Source
Thrown at haystack/utils/jinja2_chat_extension.py:383
)
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
origin = tool_call_results[0].origin
error = tool_call_results[0].errorView on GitHub (pinned to e318778c9b)
Solutions
- Ensure the assistant block produces at least one tool call or text part, e.g. guard empty variables with Jinja2 defaults.
- Check the variable holding tool calls is populated and is a list of ToolCall objects.
- Add a fallback text: {{ text or "(empty response)" }} inside the assistant block.
- Skip rendering the assistant message entirely when there is no content ({% if ... %}).
Example fix
// before
{% if reasoning %}{{ reasoning }}{% endif %} // only reasoning
// after
{{ text or "(no answer)" }}{{ tool_calls_json }} // ensures text or tool call Defensive patterns
Strategy: validation
Validate before calling
from haystack.dataclasses import ChatMessage, TextContent, ToolCall
def validate_assistant_nonempty(msg: ChatMessage):
has_text = any(isinstance(p, TextContent) for p in msg.content_parts)
has_calls = any(isinstance(p, ToolCall) for p in msg.content_parts)
if not (has_text or has_calls):
raise TypeError("Assistant message needs at least one text or tool call part") Type guard
def assistant_has_content(msg: ChatMessage) -> bool:
from haystack.dataclasses import TextContent, ToolCall
return any(isinstance(p, (TextContent, ToolCall)) for p in msg.content_parts) Try / catch
try:
messages = renderer.run(template=tpl, variables=vars)["messages"]
except ValueError as e:
if "at least one text or tool call" in str(e):
log.error("Assistant block empty; check tool_calls/text variables")
raise Prevention
- Guard LLM output variables with Jinja2 fallbacks: {{ text or "..." }}.
- Verify tool_calls variables are populated lists of ToolCall before rendering.
- Wrap assistant blocks in {% if %} so empty turns are skipped.
- Check upstream LLM/agent outputs for empty responses in tests.
When it happens
Trigger: An assistant block whose Jinja2 expressions all evaluate to empty (e.g. empty tool_calls variable and empty text); a conditional that emits only reasoning content; inserting messages that are not assistant text/tool-call messages.
Common situations: Optional LLM output variables that came back empty; templates expecting tool calls from a variable that was None; rendering assistant turns from an agent run that produced only reasoning.
Related errors
- Assistant message must contain one text part at most.
- Assistant message must contain only text, tool call or reaso
- User message must contain only TextContent, string, ImageCon
- System message must contain a text part.
- System message must contain only one text part.
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/448173416330e397.
Report an issue: GitHub.