{"record":{"id":"8033fed6149538db","repo":"deepset-ai/haystack","slug":"user-prompt-must-define-exactly-one-message-block","errorCode":null,"errorMessage":"user_prompt must define exactly one message block, found {len(message_blocks)}.","messagePattern":"user_prompt must define exactly one message block, found (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/components/agents/utils.py","lineNumber":255,"sourceCode":"# Regex to detect the Jinja2 chat template syntax\n_JINJA2_CHAT_TEMPLATE_RE = re.compile(r\"\\{%\\s*message\\s\")\n# Regex to extract the role from a Jinja2 message block, e.g. {% message role=\"user\" %}\n_JINJA2_MESSAGE_ROLE_RE = re.compile(r'\\{%\\s*message\\s+role\\s*=\\s*[\"\\'](\\w+)[\"\\']')\n\n\ndef _validate_prompt_message_blocks(user_prompt: str | None, system_prompt: str | None) -> None:\n    \"\"\"\n    Validate explicit Jinja2 message blocks in Agent prompts.\n\n    :param user_prompt: Optional user prompt template.\n    :param system_prompt: Optional system prompt template.\n    :raises ValueError: If a prompt contains multiple message blocks or a literal block role is invalid.\n    \"\"\"\n    if user_prompt is not None:\n        message_blocks = _JINJA2_CHAT_TEMPLATE_RE.findall(user_prompt)\n        roles = _JINJA2_MESSAGE_ROLE_RE.findall(user_prompt)\n        if len(message_blocks) > 1:\n            raise ValueError(f\"user_prompt must define exactly one message block, found {len(message_blocks)}.\")\n        if roles and roles[0] != \"user\":\n            raise ValueError(f\"user_prompt message block must have role 'user', found role '{roles[0]}'.\")\n\n    if system_prompt is not None and _JINJA2_CHAT_TEMPLATE_RE.search(system_prompt):\n        message_blocks = _JINJA2_CHAT_TEMPLATE_RE.findall(system_prompt)\n        roles = _JINJA2_MESSAGE_ROLE_RE.findall(system_prompt)\n        if len(message_blocks) > 1:\n            raise ValueError(f\"system_prompt must define exactly one message block, found {len(message_blocks)}.\")\n        if roles and roles[0] != \"system\":\n            raise ValueError(f\"system_prompt message block must have role 'system', found role '{roles[0]}'.\")\n\n\ndef _template_for_role(prompt: str, role: str) -> str:\n    \"\"\"\n    Convert a prompt into a ChatPromptBuilder string template for the expected role.\n\n    :param prompt: Prompt template, with or without an explicit Jinja2 message block.\n    :param role: Role to use when wrapping a plain string prompt.","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/agents/utils.py#L237-L273","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copying a full chat template into user_prompt; concatenating multiple message snippets; migrating a whole chat renderer template instead of just the user turn.","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."],"exampleFix":"// before\nuser_prompt = \"{% message role='user' %}Hi{% endmessage %}{% message role='user' %}Bye{% endmessage %}\"\n// after\nuser_prompt = \"{% message role='user' %}Hi{% endmessage %}\"","handlingStrategy":"validation","validationCode":"import re\n_JINJA2_CHAT_TEMPLATE_RE = re.compile(r\"{\\%\\s*message\\s+.*?\\%}.*?{\\%\\s*endmessage\\s*\\%}\", re.DOTALL)\nblocks = _JINJA2_CHAT_TEMPLATE_RE.findall(user_prompt)\nassert len(blocks) <= 1, f\"user_prompt has {len(blocks)} message blocks\"","typeGuard":null,"tryCatchPattern":"try:\n    agent = Agent(gen, user_prompt=prompt)\nexcept ValueError as e:\n    if \"exactly one message block\" in str(e):\n        logging.error(\"Prompt template malformed: %s\", e)\n    raise","preventionTips":["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."],"tags":["haystack","agents","prompt","validation"],"backgroundTag":"multiple-message-blocks","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}