deepset-ai/haystack · error · ValueError

Message template produced content that couldn't be parsed in

Error message

Message template produced content that couldn't be parsed into any message parts. Content: {content!r}

What it means

ValueError raised by `_build_chat_message_json` when a {% message %} template body renders to content that, after splitting on the internal sentinel tags, yields no parseable message parts. This means the template produced nothing recognizable (empty or only whitespace/non-part text) for the message.

Source

Thrown at haystack/utils/jinja2_chat_extension.py:242

        """
        Build a ChatMessage object from template content and serialize it to a JSON string.

        This method is called by Jinja2 when processing a `{% message %}` tag.
        It takes the rendered content from the template, converts XML blocks into ChatMessageContentT objects,
        creates a ChatMessage object and serializes it to a JSON string.

        :param role: The role of the message
        :param name: Optional name for the message sender
        :param meta: Optional metadata dictionary
        :param caller: Callable that returns the rendered content
        :return: A JSON string representation of the ChatMessage object
        """

        content = caller()
        start_tag, end_tag = _sentinel_tags(getattr(self.environment, _NONCE_ATTR))
        parts = self._parse_content_parts(content, start_tag, end_tag)
        if not parts:
            raise ValueError(
                f"Message template produced content that couldn't be parsed into any message parts. "
                f"Content: {_redact_nonce(content, start_tag, end_tag)!r}"
            )

        chat_message = self._validate_build_chat_message(parts=parts, role=role, meta=meta, name=name)

        return json.dumps(chat_message.to_dict()) + "\n"

    def _build_inserted_messages_json(
        self,
        messages: list[ChatMessage] | ChatMessage,
        caller: Callable[[], str],  # noqa: ARG002
    ) -> str:
        """
        Expand a list of ChatMessage objects into newline-separated JSON, one message per line.

        This method is called by Jinja2 when processing an `{% insert %}` tag. It produces the same JSON-line format
        as `_build_chat_message_json`, so the messages are parsed back into ChatMessage objects by the

View on GitHub (pinned to e318778c9b)

Solutions

  1. Ensure the message body always renders at least one content part (text, image link, or tool part sentinel)
  2. Guard empty variables with a Jinja default filter: {{ optional | default('fallback text') }}
  3. Check that custom preprocessing/postprocessing does not strip or alter the sentinel tags produced by the extension

Example fix

// before
{% message role='user' %}{{ maybe_empty }}{% endmessage %}
// after
{% message role='user' %}{{ maybe_empty | default('No question provided') }}{% endmessage %}
Defensive patterns

Strategy: try-catch

Validate before calling

def message_body_is_renderable(template_body: str) -> bool:
    return bool(template_body.strip())

Type guard

null

Try / catch

try:
    messages = rendered_chat_prompt  # runs _build_chat_message_json internally
except ValueError as e:
    if "couldn't be parsed into any message parts" in str(e):
        messages = [fallback_message]
    else:
        raise

Prevention

When it happens

Trigger: Rendering a {% message %} block whose body is empty or evaluates to an empty string (e.g. `{% message role='user' %}{{ optional }}{% endmessage %}` where optional is '' or None), or content that escapes the sentinel tagging so no parts match.

Common situations: Conditionally empty variables in the template body; templates where all content is inside {% if %} that evaluates false; overriding/sanitization that strips the sentinel tags so parsing finds no parts.

Related errors


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