{"record":{"id":"f7d3682ece9cca08","repo":"can1357/oh-my-pi","slug":"parse-error-unclosed-comment","errorCode":null,"errorMessage":"Parse error: unclosed comment","messagePattern":"Parse error: unclosed comment","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/utils/src/template.ts","lineNumber":241,"sourceCode":"\t}\n\tthrow new Error(\"Parse error: unclosed template expression\");\n}\n\nfunction parseTemplate(source: string): Node[] {\n\tconst root: Node[] = [];\n\tconst stack: { node: BlockNode; target: Node[]; inverted: boolean }[] = [];\n\tlet target = root;\n\tlet cursor = 0;\n\twhile (cursor < source.length) {\n\t\tconst open = source.indexOf(\"{{\", cursor);\n\t\tif (open < 0) {\n\t\t\tif (cursor < source.length) target.push({ kind: \"text\", value: source.slice(cursor) });\n\t\t\tbreak;\n\t\t}\n\t\tif (open > cursor) target.push({ kind: \"text\", value: source.slice(cursor, open) });\n\t\tif (source.startsWith(\"{{!--\", open)) {\n\t\t\tconst end = source.indexOf(\"--}}\", open + 6);\n\t\t\tif (end < 0) throw new Error(\"Parse error: unclosed comment\");\n\t\t\tcursor = end + 4;\n\t\t\tcontinue;\n\t\t}\n\t\tconst triple = source.startsWith(\"{{{\", open);\n\t\tconst contentStart = open + (triple ? 3 : 2);\n\t\tconst end = findTagEnd(source, contentStart, triple);\n\t\tconst raw = source.slice(contentStart, end).trim();\n\t\tcursor = end + (triple ? 3 : 2);\n\t\tif (!raw || raw.startsWith(\"!\")) continue;\n\t\tif (raw === \"else\" || raw.startsWith(\"else \")) {\n\t\t\tconst current = stack[stack.length - 1];\n\t\t\tif (!current) throw new Error(\"Parse error: unexpected else\");\n\t\t\ttarget = current.node.inverse;\n\t\t\tif (raw.length > 4) {\n\t\t\t\tconst parentTarget = target;\n\t\t\t\tconst nested: BlockNode = {\n\t\t\t\t\tkind: \"block\",\n\t\t\t\t\texpression: parseCall(raw.slice(5)),","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/template.ts#L223-L259","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{{!-- notes about the section\n<div>...</div>\n// after\n{{!-- notes about the section --}}\n<div>...</div>","handlingStrategy":"try-catch","validationCode":"function hasBalancedHandlebarsComments(src) {\n  let i = 0;\n  while ((i = src.indexOf('{{!--', i)) !== -1) {\n    const end = src.indexOf('--}}', i + 6);\n    if (end === -1) return false;\n    i = end + 4;\n  }\n  return true;\n}\nif (!hasBalancedHandlebarsComments(templateSrc)) throw new Error('template has an unclosed {{!-- comment');","typeGuard":null,"tryCatchPattern":"try {\n  render(templateSrc, data);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Parse error:')) {\n    // surface a template-authoring problem, not a runtime data problem\n    throw new Error(`Invalid template: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["template","parse-error","handlebars"],"backgroundTag":"unclosed-comment-tag","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}