langchain-ai/langchain · error · ChevronError
Unexpected EOF\nthe tag "{open_sections[-1]}" was never clos
Error message
Unexpected EOF\nthe tag "{open_sections[-1]}" was never closed\nwas opened at line {_LAST_TAG_LINE} What it means
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.
Source
Thrown at libs/core/langchain_core/utils/mustache.py:322
# Start yielding
# Ignore literals that are empty
if literal:
yield ("literal", literal)
# Ignore comments and set delimiters
if tag_type not in {"comment", "set delimiter?"}:
yield (tag_type, tag_key)
# If there are any open sections when we're done
if open_sections:
# Then we need to complain
msg = (
"Unexpected EOF\n"
f'the tag "{open_sections[-1]}" was never closed\n'
f"was opened at line {_LAST_TAG_LINE}"
)
raise ChevronError(msg)
#
# Helper functions
#
def _html_escape(string: str) -> str:
"""Return the HTML-escaped string with these characters escaped: `" & < >`."""
html_codes = {
'"': """,
"<": "<",
">": ">",
}
# & must be handled first
string = string.replace("&", "&")
for char, code in html_codes.items():View on GitHub (pinned to e32fa9a52e)
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.
Example fix
# before
template = "{{#has_docs}}Docs:" # never closed
# after
template = "{{#has_docs}}Docs:{{/has_docs}}" Defensive patterns
Strategy: validation
Validate before calling
import re
def all_sections_closed(template: str) -> bool:
stack = []
for typ, key in re.findall(r"\{\{\s*(#|\^|/)\s*(.*?)\s*\}\}", template):
if typ in "#^":
stack.append(key)
elif stack:
stack.pop()
return not stack Try / catch
try:
list(tokenize(template))
except ChevronError as e:
raise ValueError(f"invalid template: {e}") from e Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Trying to close tag "{tag_key}"\nLooks like it was not opene
- unclosed tag at line {_CURRENT_LINE}
- empty tag at line {_CURRENT_LINE}
- unclosed set delimiter tag\nat line {_CURRENT_LINE}
- Trying to close tag "{tag_key}"\nlast open tag is "{last_sec
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/3da91b000ee38463.
Report an issue: GitHub.