deepset-ai/haystack · error · TemplateSyntaxError
meta must be a dictionary
Error message
meta must be a dictionary
What it means
TemplateSyntaxError raised by `_parse_message_tag` when the optional meta= attribute of a {% message %} tag is not a Jinja2 Dict node. Meta must be written as a dictionary literal in the template so it can be attached to the built ChatMessage.
Source
Thrown at haystack/utils/jinja2_chat_extension.py:207
raise TemplateSyntaxError(f"Role must be one of: {', '.join(self.SUPPORTED_ROLES)}", lineno)
# Parse optional name attribute
name_expr = None
if parser.stream.current.test("name:name"):
parser.stream.skip()
parser.stream.expect("assign")
name_expr = parser.parse_expression()
if not isinstance(name_expr.value, str):
raise TemplateSyntaxError("name must be a string", lineno)
# Parse optional meta attribute
meta_expr = None
if parser.stream.current.test("name:meta"):
parser.stream.skip()
parser.stream.expect("assign")
meta_expr = parser.parse_expression()
if not isinstance(meta_expr, nodes.Dict):
raise TemplateSyntaxError("meta must be a dictionary", lineno)
# Parse message body
body = parser.parse_statements(("name:endmessage",), drop_needle=True)
# Build message node with all parameters
return nodes.CallBlock(
self.call_method(
name="_build_chat_message_json",
args=[role_expr, name_expr or nodes.Const(None), meta_expr or nodes.Dict([])],
),
[],
[],
body,
).set_lineno(lineno)
def _build_chat_message_json(self, role: str, name: str | None, meta: dict, caller: Callable[[], str]) -> str:
"""
Build a ChatMessage object from template content and serialize it to a JSON string.View on GitHub (pinned to e318778c9b)
Solutions
- Write meta as an inline dictionary literal: meta={'source': 'wiki', 'page': 1}
- Remove the meta attribute if metadata is not required
- Set metadata on the resulting ChatMessage in Python code instead of in the template
Example fix
// before
{% message role='user' meta=metadata %}Hello{% endmessage %}
// after
{% message role='user' meta={'source': 'wiki'} %}Hello{% endmessage %} Defensive patterns
Strategy: validation
Validate before calling
import re
for m in re.finditer(r"meta\s*=\s*([^\s%}]+)", template):
assert m.group(1).startswith("{"), f"meta must be a dict literal, got {m.group(1)}" Type guard
null
Try / catch
from jinja2.exceptions import TemplateSyntaxError
try:
env.parse(template)
except TemplateSyntaxError as e:
if "meta must be a dictionary" in str(e):
raise ValueError("Use meta={'key': 'value'} inline in the template") from e
raise Prevention
- Write meta as an inline dict literal: meta={'k': 'v'}
- Don't pass context variables or strings as meta; set metadata in Python after building the message
- Keep meta simple and serializable
When it happens
Trigger: Writing `{% message role='user' meta='foo' %}` or `meta=meta_dict` (a name/expression instead of a literal dict), so the parsed node is a Const or Name rather than nodes.Dict.
Common situations: Trying to pass a context variable as meta instead of a literal; quoting the dict into a string; omitting braces: meta=source instead of meta={'source': 'docs'}.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- Role must be one of: {', '.join(self.SUPPORTED_ROLES)}
- name must be a string
- The 'insert' tag requires an expression that evaluates to a
- Message template produced content that couldn't be parsed in
- expected token 'name:role'
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/f7d4bdbbe31e5960.
Report an issue: GitHub.