{"record":{"id":"ec991f2d68cc6084","repo":"emberjs/ember.js","slug":"closing-tag-tag-name-without-an-open-tag","errorCode":null,"errorMessage":"Closing tag </${tag.name}> without an open tag","messagePattern":"Closing tag </(.+?)> without an open tag","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts","lineNumber":592,"sourceCode":"      loc: this.source.spanFor(first.loc).extend(this.source.spanFor(last.loc)),\n    });\n  }\n\n  validateEndTag(\n    tag: StartTag | EndTag,\n    element: ASTv1.ParentNode,\n    selfClosing: boolean\n  ): asserts element is ASTv1.ElementNode {\n    if (voidMap.has(tag.name) && !selfClosing) {\n      // EngTag is also called by StartTag for void and self-closing tags (i.e.\n      // <input> or <br />, so we need to check for that here. Otherwise, we would\n      // throw an error for those cases.\n      throw generateSyntaxError(\n        `<${tag.name}> elements do not need end tags. You should remove it`,\n        tag.loc\n      );\n    } else if (element.type !== 'ElementNode') {\n      throw generateSyntaxError(`Closing tag </${tag.name}> without an open tag`, tag.loc);\n    } else if (element.tag !== tag.name) {\n      throw generateSyntaxError(\n        `Closing tag </${tag.name}> did not match last open tag <${element.tag}> (on line ${element.loc.startPosition.line})`,\n        tag.loc\n      );\n    }\n  }\n\n  assembleAttributeValue(\n    parts: ASTv1.AttrPart[],\n    isQuoted: boolean,\n    isDynamic: boolean,\n    span: src.SourceSpan\n  ): ASTv1.AttrValue {\n    if (isDynamic) {\n      if (isQuoted) {\n        return this.assembleConcatenatedValue(parts);\n      } else {","sourceCodeStart":574,"sourceCodeEnd":610,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts#L574-L610","documentation":"When finishEndTag validates a closing tag, if the corresponding node on the element stack is not an ElementNode, there is no matching open element — the closing tag has nothing to close.","triggerScenarios":"A `</div>` (or any `</name>`) appears when the innermost open node is text, a mustache/comment, or nothing at all — i.e. the start tag is missing or was already closed.","commonSituations":"Deleting an opening tag but leaving the closing one, mismatched mustache blocks consuming the element stack, merge conflicts that drop one side of a tag pair.","solutions":["Add the missing opening tag matching the closing tag name","Remove the stray closing tag","Rebalance tags after a merge conflict; check nesting of `{{#if}}`/`{{#each}}` blocks around the reported line"],"exampleFix":"// before\n<p>text</div>\n// after\n<p>text</p>","handlingStrategy":"try-catch","validationCode":"// balance-check open/close tags before parsing\nconst opens = (template.match(/<\\w+/g) || []).length;\nconst closes = (template.match(/<\\/\\w+>/g) || []).length;\nif (closes > opens) throw new Error('More closing tags than opening tags');","typeGuard":null,"tryCatchPattern":"try { parse(template) } catch (e) { if (/without an open tag/.test(e.message)) { /* remove stray closing tag or add opener, then retry parse */ } else throw e; }","preventionTips":["Verify every `</x>` has a matching `<x>`","Check `{{#block}}` nesting doesn't swallow element opens/closes","Resolve merge conflicts by rebuilding the element, not hand-merging tags"],"tags":["glimmer","handlebars","html","unclosed-tag"],"backgroundTag":"tag-mismatch","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}