deepset-ai/haystack · error · ValueError

Message content in template is empty or contains only whites

Error message

Message content in template is empty or contains only whitespace characters. Content: {content!r}

What it means

During template rendering, a message block produced content that is empty or whitespace-only after parsing the sentinel-tagged content parts. Haystack rejects this because a ChatMessage cannot be built from empty content.

Source

Thrown at haystack/utils/jinja2_chat_extension.py:301

    @staticmethod
    def _parse_content_parts(content: str, start_tag: str, end_tag: str) -> list[ChatMessageContentT]:
        """
        Parse a string into a sequence of ChatMessageContentT objects.

        This method handles:
        - Plain text content, converted to TextContent objects
        - Structured content parts wrapped in sentinel tags, converted to ChatMessageContentT objects

        :param content: Input string containing mixed text and content parts
        :param start_tag: The opening sentinel tag (including the nonce)
        :param end_tag: The closing sentinel tag (including the nonce)
        :return: A list of ChatMessageContentT objects
        :raises ValueError: If the content is empty or contains only whitespace characters or if a
                            `<haystack_content_part>` tag is found without a matching closing tag.
        """
        if not content.strip():
            raise ValueError(
                f"Message content in template is empty or contains only whitespace characters. "
                f"Content: {_redact_nonce(content, start_tag, end_tag)!r}"
            )

        parts: list[ChatMessageContentT] = []
        cursor = 0
        total_length = len(content)

        while cursor < total_length:
            tag_start = content.find(start_tag, cursor)

            if tag_start == -1:
                # No more tags, add remaining text if any
                remaining_text = content[cursor:].strip()
                if remaining_text:
                    parts.append(TextContent(text=remaining_text))
                break

View on GitHub (pinned to e318778c9b)

Solutions

  1. Ensure the expression inside the message block always yields non-empty text, e.g. {{ var or "default" }}.
  2. Guard with Jinja2 conditionals: {% if var %}...{% endif %} around the message content.
  3. Check the pipeline variables passed to the template renderer for empty/None values.
  4. Verify the variable names in the template match the provided variables dict exactly.

Example fix

// before
"""User message: {{ query }}"""  # query is empty
// after
"""User message: {{ query or "(no query provided)" }}"""
Defensive patterns

Strategy: validation

Validate before calling

def validate_template_vars(vars: dict):
    for k, v in vars.items():
        if v is None or (isinstance(v, str) and not v.strip()):
            raise ValueError(f"Template variable '{k}' is empty or whitespace-only")

Type guard

def has_content(v) -> bool:
    return v is not None and (not isinstance(v, str) or bool(v.strip()))

Try / catch

try:
    messages = renderer.run(template=tpl, variables=vars)["messages"]
except ValueError as e:
    if "empty or contains only whitespace" in str(e):
        raise ValueError(f"Template produced empty message; check variables: {list(vars)}") from e
    raise

Prevention

When it happens

Trigger: A Jinja2 expression inside a message block evaluates to an empty string, None, or only whitespace (e.g. {{ optional_var }} where the variable is empty), and the message carries no content parts.

Common situations: Optional context variables that end up empty; a filter that strips content (e.g. truncate to 0); loops that produce nothing; templates where a variable name is misspelled and renders as empty.

Related errors


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