deepset-ai/haystack · error · ValueError
user_prompt must define exactly one message block, found {le
Error message
user_prompt must define exactly one message block, found {len(message_blocks)}. What it means
Raised by _validate_prompt_message_blocks when a user_prompt string contains more than one Jinja2 message block (e.g. multiple {% message %} tags). The Agent expects each prompt to define exactly one message block so it can map cleanly to a single ChatMessage.
Source
Thrown at haystack/components/agents/utils.py:255
# Regex to detect the Jinja2 chat template syntax
_JINJA2_CHAT_TEMPLATE_RE = re.compile(r"\{%\s*message\s")
# Regex to extract the role from a Jinja2 message block, e.g. {% message role="user" %}
_JINJA2_MESSAGE_ROLE_RE = re.compile(r'\{%\s*message\s+role\s*=\s*["\'](\w+)["\']')
def _validate_prompt_message_blocks(user_prompt: str | None, system_prompt: str | None) -> None:
"""
Validate explicit Jinja2 message blocks in Agent prompts.
:param user_prompt: Optional user prompt template.
:param system_prompt: Optional system prompt template.
:raises ValueError: If a prompt contains multiple message blocks or a literal block role is invalid.
"""
if user_prompt is not None:
message_blocks = _JINJA2_CHAT_TEMPLATE_RE.findall(user_prompt)
roles = _JINJA2_MESSAGE_ROLE_RE.findall(user_prompt)
if len(message_blocks) > 1:
raise ValueError(f"user_prompt must define exactly one message block, found {len(message_blocks)}.")
if roles and roles[0] != "user":
raise ValueError(f"user_prompt message block must have role 'user', found role '{roles[0]}'.")
if system_prompt is not None and _JINJA2_CHAT_TEMPLATE_RE.search(system_prompt):
message_blocks = _JINJA2_CHAT_TEMPLATE_RE.findall(system_prompt)
roles = _JINJA2_MESSAGE_ROLE_RE.findall(system_prompt)
if len(message_blocks) > 1:
raise ValueError(f"system_prompt must define exactly one message block, found {len(message_blocks)}.")
if roles and roles[0] != "system":
raise ValueError(f"system_prompt message block must have role 'system', found role '{roles[0]}'.")
def _template_for_role(prompt: str, role: str) -> str:
"""
Convert a prompt into a ChatPromptBuilder string template for the expected role.
:param prompt: Prompt template, with or without an explicit Jinja2 message block.
:param role: Role to use when wrapping a plain string prompt.View on GitHub (pinned to e318778c9b)
Solutions
- Keep only a single {% message %} block with role 'user' in user_prompt.
- Move any additional message blocks into system_prompt (for the system turn) or handle prior turns via conversation history.
- If dynamic rendering is needed, use Jinja variables inside one message block instead of multiple blocks.
Example fix
// before
user_prompt = "{% message role='user' %}Hi{% endmessage %}{% message role='user' %}Bye{% endmessage %}"
// after
user_prompt = "{% message role='user' %}Hi{% endmessage %}" Defensive patterns
Strategy: validation
Validate before calling
import re
_JINJA2_CHAT_TEMPLATE_RE = re.compile(r"{\%\s*message\s+.*?\%}.*?{\%\s*endmessage\s*\%}", re.DOTALL)
blocks = _JINJA2_CHAT_TEMPLATE_RE.findall(user_prompt)
assert len(blocks) <= 1, f"user_prompt has {len(blocks)} message blocks" Try / catch
try:
agent = Agent(gen, user_prompt=prompt)
except ValueError as e:
if "exactly one message block" in str(e):
logging.error("Prompt template malformed: %s", e)
raise Prevention
- Keep one {% message %} block per prompt string.
- Use Jinja variables, not additional blocks, for dynamic content.
- Test prompt strings at init, not at run time.
When it happens
Trigger: Passing a user_prompt to Agent's __init__ whose string contains two or more {% message %}...{% endmessage %} blocks, e.g. a template covering both user and assistant turns.
Common situations: Copying a full chat template into user_prompt; concatenating multiple message snippets; migrating a whole chat renderer template instead of just the user turn.
Related errors
- system_prompt must define exactly one message block, found {
- The following tool names are not valid: {invalid_tool_names}
- user_prompt message block must have role 'user', found role
- system_prompt message block must have role 'system', found r
- {prompt_label} must render to exactly one {expected_role.val
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/8033fed6149538db.
Report an issue: GitHub.