{"record":{"id":"3da91b000ee38463","repo":"langchain-ai/langchain","slug":"unexpected-eof-nthe-tag-open-sections-1-was","errorCode":null,"errorMessage":"Unexpected EOF\\nthe tag \"{open_sections[-1]}\" was never closed\\nwas opened at line {_LAST_TAG_LINE}","messagePattern":"Unexpected EOF\\\\nthe tag \"(.+?)\" was never closed\\\\nwas opened at line (.+?)","errorType":"exception","errorClass":"ChevronError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/utils/mustache.py","lineNumber":322,"sourceCode":"\n        # Start yielding\n        # Ignore literals that are empty\n        if literal:\n            yield (\"literal\", literal)\n\n        # Ignore comments and set delimiters\n        if tag_type not in {\"comment\", \"set delimiter?\"}:\n            yield (tag_type, tag_key)\n\n    # If there are any open sections when we're done\n    if open_sections:\n        # Then we need to complain\n        msg = (\n            \"Unexpected EOF\\n\"\n            f'the tag \"{open_sections[-1]}\" was never closed\\n'\n            f\"was opened at line {_LAST_TAG_LINE}\"\n        )\n        raise ChevronError(msg)\n\n\n#\n# Helper functions\n#\n\n\ndef _html_escape(string: str) -> str:\n    \"\"\"Return the HTML-escaped string with these characters escaped: `\" & < >`.\"\"\"\n    html_codes = {\n        '\"': \"&quot;\",\n        \"<\": \"&lt;\",\n        \">\": \"&gt;\",\n    }\n\n    # & must be handled first\n    string = string.replace(\"&\", \"&amp;\")\n    for char, code in html_codes.items():","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/utils/mustache.py#L304-L340","documentation":"Raised by the Mustache tokenizer in `langchain_core.utils.mustache` at end of input when one or more sections were opened (`{{#name}}` or `{{^name}}`) but never closed before the template ended. The message names the first unclosed tag (stack top) and the line where it was opened. Rendering aborts; no partial output is produced.","triggerScenarios":"A template ending with `{{#items}}...` and no `{{/items}}`, parsed via `tokenize` or rendered with `MustachePlaceholderFormatter`; also when a closing tag was mistyped so it never matched (leaving the opener on the stack).","commonSituations":"Long prompt templates where a section closer was accidentally deleted; templates truncated by string slicing or by token-limit trimming; closers with typos (`{{#section}}` ... `{{/seciton}}`) so an earlier mismatch error is masked into this EOF error.","solutions":["Go to the reported open line and add the missing `{{/name}}` at the section's end.","If the message is the EOF variant but tags look balanced, look for an unmatched opener earlier — fix any name mismatch that kept a section open.","Run the tokenizer over templates at load/CI time to catch unclosed sections before runtime."],"exampleFix":"# before\ntemplate = \"{{#has_docs}}Docs:\"  # never closed\n\n# after\ntemplate = \"{{#has_docs}}Docs:{{/has_docs}}\"","handlingStrategy":"validation","validationCode":"import re\n\ndef all_sections_closed(template: str) -> bool:\n    stack = []\n    for typ, key in re.findall(r\"\\{\\{\\s*(#|\\^|/)\\s*(.*?)\\s*\\}\\}\", template):\n        if typ in \"#^\":\n            stack.append(key)\n        elif stack:\n            stack.pop()\n    return not stack","typeGuard":null,"tryCatchPattern":"try:\n    list(tokenize(template))\nexcept ChevronError as e:\n    raise ValueError(f\"invalid template: {e}\") from e","preventionTips":["Never truncate/slice templates after tag extraction — validate afterwards.","Check that every {{#x}} has a matching {{/x}} before serving the template.","Tokenize at template load."],"tags":["mustache","sections","eof","prompt-template"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}