can1357/oh-my-pi · error
Parse error: unclosed comment
Error message
Parse error: unclosed comment
What it means
parseTemplate() scans the template for Handlebars-style `{{...}}` expressions. A `{{!-- ... --}}` comment open tag must be terminated by `--}}`; if the parser finds no closing delimiter anywhere after the opener it throws "Parse error: unclosed comment". This is a hard parse failure — the whole template fails to compile.
Source
Thrown at packages/utils/src/template.ts:241
}
throw new Error("Parse error: unclosed template expression");
}
function parseTemplate(source: string): Node[] {
const root: Node[] = [];
const stack: { node: BlockNode; target: Node[]; inverted: boolean }[] = [];
let target = root;
let cursor = 0;
while (cursor < source.length) {
const open = source.indexOf("{{", cursor);
if (open < 0) {
if (cursor < source.length) target.push({ kind: "text", value: source.slice(cursor) });
break;
}
if (open > cursor) target.push({ kind: "text", value: source.slice(cursor, open) });
if (source.startsWith("{{!--", open)) {
const end = source.indexOf("--}}", open + 6);
if (end < 0) throw new Error("Parse error: unclosed comment");
cursor = end + 4;
continue;
}
const triple = source.startsWith("{{{", open);
const contentStart = open + (triple ? 3 : 2);
const end = findTagEnd(source, contentStart, triple);
const raw = source.slice(contentStart, end).trim();
cursor = end + (triple ? 3 : 2);
if (!raw || raw.startsWith("!")) continue;
if (raw === "else" || raw.startsWith("else ")) {
const current = stack[stack.length - 1];
if (!current) throw new Error("Parse error: unexpected else");
target = current.node.inverse;
if (raw.length > 4) {
const parentTarget = target;
const nested: BlockNode = {
kind: "block",
expression: parseCall(raw.slice(5)),View on GitHub (pinned to 9690622007)
Solutions
- Locate every `{{!--` in the template source and ensure each has a matching `--}}` before the end of the file.
- If the comment was meant to be prose, escape the braces (e.g. `{{'{{!--'}}` or plain text) so the parser does not see an open tag.
- If the source comes from a file or env var, log/inspect the full template string to spot truncation at read time.
Example fix
// before
{{!-- notes about the section
<div>...</div>
// after
{{!-- notes about the section --}}
<div>...</div> Defensive patterns
Strategy: try-catch
Validate before calling
function hasBalancedHandlebarsComments(src) {
let i = 0;
while ((i = src.indexOf('{{!--', i)) !== -1) {
const end = src.indexOf('--}}', i + 6);
if (end === -1) return false;
i = end + 4;
}
return true;
}
if (!hasBalancedHandlebarsComments(templateSrc)) throw new Error('template has an unclosed {{!-- comment'); Try / catch
try {
render(templateSrc, data);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Parse error:')) {
// surface a template-authoring problem, not a runtime data problem
throw new Error(`Invalid template: ${err.message}`);
}
throw err;
} Prevention
- Add a lint/CI check that scans template assets for unmatched `{{!--` markers.
- Never truncate templates programmatically; validate written templates parse before persisting.
- Escape literal brace sequences in prose so they cannot be mistaken for comments.
When it happens
Trigger: Calling any template render/compile entry (parseTemplate is reached from public parse/render APIs) with source containing `{{!--` but no matching `--}}` later in the string. Note the search starts at open+6, so an immediately adjacent `--}}` is not treated as the closer.
Common situations: Truncating templates when saving/generating them (a file cut off mid-comment); copy-pasting Handlebars docs where the closing delimiter was dropped; escaping problems where a literal `{{!--` appears in generated docs or license text; a templating migration that left a commented-out block unterminated.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Parse error: unexpected else
- Parse error: mismatched ${raw}
- Parse error: unclosed block ${stack[stack.length - 1].node.e
- Missing helper: "${call.name}"
- The partial ${node.expression.name} could not be found
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f7d3682ece9cca08.
Report an issue: GitHub.