emberjs/ember.js · error
Closing tag </${tag.name}> without an open tag
Error message
Closing tag </${tag.name}> without an open tag What it means
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.
Source
Thrown at packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts:592
loc: this.source.spanFor(first.loc).extend(this.source.spanFor(last.loc)),
});
}
validateEndTag(
tag: StartTag | EndTag,
element: ASTv1.ParentNode,
selfClosing: boolean
): asserts element is ASTv1.ElementNode {
if (voidMap.has(tag.name) && !selfClosing) {
// EngTag is also called by StartTag for void and self-closing tags (i.e.
// <input> or <br />, so we need to check for that here. Otherwise, we would
// throw an error for those cases.
throw generateSyntaxError(
`<${tag.name}> elements do not need end tags. You should remove it`,
tag.loc
);
} else if (element.type !== 'ElementNode') {
throw generateSyntaxError(`Closing tag </${tag.name}> without an open tag`, tag.loc);
} else if (element.tag !== tag.name) {
throw generateSyntaxError(
`Closing tag </${tag.name}> did not match last open tag <${element.tag}> (on line ${element.loc.startPosition.line})`,
tag.loc
);
}
}
assembleAttributeValue(
parts: ASTv1.AttrPart[],
isQuoted: boolean,
isDynamic: boolean,
span: src.SourceSpan
): ASTv1.AttrValue {
if (isDynamic) {
if (isQuoted) {
return this.assembleConcatenatedValue(parts);
} else {View on GitHub (pinned to 26f97246a8)
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
Example fix
// before <p>text</div> // after <p>text</p>
Defensive patterns
Strategy: try-catch
Validate before calling
// balance-check open/close tags before parsing
const opens = (template.match(/<\w+/g) || []).length;
const closes = (template.match(/<\/\w+>/g) || []).length;
if (closes > opens) throw new Error('More closing tags than opening tags'); Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- <${tag.name}> elements do not need end tags. You should remo
- Closing tag </${tag.name}> did not match last open tag <${el
- Compile Error: ${error.problem} @ ${error.span.start}..${err
- Compile Error: ${template.problem} @ ${template.span.start}.
- Changing context using "../" is not supported in Glimmer
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/ec991f2d68cc6084.
Report an issue: GitHub.