deepset-ai/haystack · error · ValueError
system_prompt must define exactly one message block, found {
Error message
system_prompt must define exactly one message block, found {len(message_blocks)}. What it means
Raised when a system_prompt passed to Agent contains more than one Jinja2 message block. Like user_prompt, the system prompt must render to exactly one message so the Agent can build a valid chat.
Source
Thrown at haystack/components/agents/utils.py:263
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.
:returns: The original message-block template, or a plain string prompt wrapped in one message block.
"""
if _JINJA2_CHAT_TEMPLATE_RE.search(prompt):
return prompt
return f'{{% message role="{role}" %}}{prompt}{{% endmessage %}}'
def _render_prompt_messages(View on GitHub (pinned to e318778c9b)
Solutions
- Reduce system_prompt to a single {% message role='system' %} block.
- Put user-turn content in user_prompt instead of system_prompt.
- Merge multiple system instructions into one message block separated by newlines.
Example fix
// before
system_prompt = "{% message role='system' %}A{% endmessage %}{% message role='system' %}B{% endmessage %}"
// after
system_prompt = "{% message role='system' %}A\nB{% endmessage %}" Defensive patterns
Strategy: validation
Validate before calling
blocks = re.findall(r"{\%\s*message\s+.*?\%}.*?{\%\s*endmessage\s*\%}", system_prompt, re.DOTALL)
assert len(blocks) <= 1, f"system_prompt has {len(blocks)} message blocks" Try / catch
try:
agent = Agent(gen, system_prompt=prompt)
except ValueError as e:
if "system_prompt must define exactly one" in str(e):
logging.error("system_prompt malformed: %s", e)
raise Prevention
- Keep system_prompt to one {% message role='system' %} block.
- Concatenate multiple instructions inside a single block.
- Never paste multi-turn chat templates into system_prompt.
When it happens
Trigger: Passing a system_prompt with two or more {% message %} blocks, e.g. a template containing both system and user turns.
Common situations: Reusing a full chat template as system_prompt; appending extra message blocks for examples; merging two system prompts by concatenation.
Related errors
- user_prompt must define exactly one message block, found {le
- 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/700cb15db5c45684.
Report an issue: GitHub.