deepset-ai/haystack · error · TemplateSyntaxError
Role must be one of: {', '.join(self.SUPPORTED_ROLES)}
Error message
Role must be one of: {', '.join(self.SUPPORTED_ROLES)} What it means
TemplateSyntaxError raised by `_parse_message_tag` when the role assigned to a {% message %} tag is a literal constant that is not in SUPPORTED_ROLES. Only a fixed set of chat roles (e.g. user, assistant, system, tool) are allowed for message templates.
Source
Thrown at haystack/utils/jinja2_chat_extension.py:189
Parse the message tag and its attributes in the Jinja2 template.
This method handles the parsing of role (mandatory), name (optional), meta (optional) and message body content.
:param parser: The Jinja2 parser instance
:param lineno: The line number of the tag, used for error reporting.
:return: A CallBlock node containing the parsed message configuration
:raises TemplateSyntaxError: If an invalid role is provided
"""
# Parse role attribute (mandatory)
parser.stream.expect("name:role")
parser.stream.expect("assign")
role_expr = parser.parse_expression()
if isinstance(role_expr, nodes.Const):
role = role_expr.value
if role not in self.SUPPORTED_ROLES:
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)View on GitHub (pinned to e318778c9b)
Solutions
- Use a supported role literal, e.g. {% message role='user' %} or role='assistant'
- Inspect the extension's SUPPORTED_ROLES to see the exact allowed values
- Remove the role attribute to use the default if a custom role is unnecessary
Example fix
// before
{% message role='bot' %}Hi{% endmessage %}
// after
{% message role='assistant' %}Hi{% endmessage %} Defensive patterns
Strategy: validation
Validate before calling
SUPPORTED_ROLES = {"user", "assistant", "system", "tool"} # check the extension for exact set
assert role.lower() in SUPPORTED_ROLES, f"role must be one of {SUPPORTED_ROLES}" Type guard
null
Try / catch
from jinja2.exceptions import TemplateSyntaxError
try:
env.parse(template)
except TemplateSyntaxError as e:
raise ValueError(f"Invalid role in template: {e.message}") from e Prevention
- Use lowercase role literals from SUPPORTED_ROLES only
- Don't invent roles from other providers (e.g. 'developer', 'function')
- Parse templates at load time to catch invalid roles early
When it happens
Trigger: Writing `{% message role='bot' %}` or `{% message role='Agent' %}` (wrong casing) or any other literal role not in SUPPORTED_ROLES in a chat template parsed by this Jinja2 extension.
Common situations: Using role names from other frameworks (OpenAI 'function', 'developer'); capitalized roles; typos like 'asistant'; hardcoding roles instead of relying on the default.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- name must be a string
- meta must be a dictionary
- 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/4f8d896fbc375525.
Report an issue: GitHub.