{"record":{"id":"a374cc6401be0ba2","repo":"deepset-ai/haystack","slug":"expected-token-assign","errorCode":null,"errorMessage":"expected token 'assign'","messagePattern":"expected token 'assign'","errorType":"panic","errorClass":"TemplateSyntaxError","httpStatus":null,"severity":"error","filePath":"haystack/utils/jinja2_chat_extension.py","lineNumber":183,"sourceCode":"        return nodes.CallBlock(\n            self.call_method(name=\"_build_inserted_messages_json\", args=[expr]), [], [], []\n        ).set_lineno(lineno)\n\n    def _parse_message_tag(self, parser: Any, lineno: int) -> nodes.Node | list[nodes.Node]:\n        \"\"\"\n        Parse the message tag and its attributes in the Jinja2 template.\n\n        This method handles the parsing of role (mandatory), name (optional), meta (optional) and message body content.\n\n        :param parser: The Jinja2 parser instance\n        :param lineno: The line number of the tag, used for error reporting.\n        :return: A CallBlock node containing the parsed message configuration\n        :raises TemplateSyntaxError: If an invalid role is provided\n        \"\"\"\n\n        # Parse role attribute (mandatory)\n        parser.stream.expect(\"name:role\")\n        parser.stream.expect(\"assign\")\n        role_expr = parser.parse_expression()\n\n        if isinstance(role_expr, nodes.Const):\n            role = role_expr.value\n            if role not in self.SUPPORTED_ROLES:\n                raise TemplateSyntaxError(f\"Role must be one of: {', '.join(self.SUPPORTED_ROLES)}\", lineno)\n\n        # Parse optional name attribute\n        name_expr = None\n        if parser.stream.current.test(\"name:name\"):\n            parser.stream.skip()\n            parser.stream.expect(\"assign\")\n            name_expr = parser.parse_expression()\n            if not isinstance(name_expr.value, str):\n                raise TemplateSyntaxError(\"name must be a string\", lineno)\n\n        # Parse optional meta attribute\n        meta_expr = None","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/utils/jinja2_chat_extension.py#L165-L201","documentation":"After reading the `role` name token, the parser calls expect(\"assign\") requiring an = sign. Jinja2 raises TemplateSyntaxError(\"expected token 'assign'\") when role is not followed by `=`, e.g. when a value or another attribute name appears instead.","triggerScenarios":"Writing {% message role \"user\" %} (missing =), or {% message role user %}, or a space/typo such as role-\"user\" in a ChatPromptBuilder template.","commonSituations":"Template syntax confusion from other templating engines; hand-editing templates and dropping the `=`.","solutions":["Add the missing `=` after role: role=\"user\"","Quote the role value as a Python/Jinja string literal","Validate the whole tag: {% message role=\"user\" %}...{% endmessage %}"],"exampleFix":"// before\n{% message role \"user\" %}Hi{% endmessage %}\n// after\n{% message role=\"user\" %}Hi{% endmessage %}","handlingStrategy":"validation","validationCode":"import re\nMSG_ROLE = re.compile(r\"{%\\s*message\\s+role\\s*=\\s*['\\\"]\\w+['\\\"]\")\nassert MSG_ROLE.search(template), \"message tag needs role=\\\"...\\\"\"","typeGuard":null,"tryCatchPattern":"try:\n    env.from_string(template)\nexcept TemplateSyntaxError as e:\n    print(f\"line {e.lineno}: {e.message}\")","preventionTips":["Follow keyword-attribute syntax name=value exactly","Quote the role value","Run template compilation as part of unit tests"],"tags":["python","jinja2","template-syntax","haystack"],"backgroundTag":"template-syntax-error","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}