emberjs/ember.js · error
Invalid end tag: closing tag must not be self-closing
Error message
Invalid end tag: closing tag must not be self-closing
What it means
Self-closing syntax (/>) is only valid on start (opening) tags. When markTagAsSelfClosing runs and the current tag is an EndTag — e.g. </div/> — the tokenizer cannot mark a closing tag as self-closing, so it throws a syntax error spanning the tag.
Source
Thrown at packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts:199
element.closeTag = null;
} else if (element.selfClosing) {
assert(element.closeTag === null, 'element.closeTag unexpectedly present');
} else {
element.closeTag = closeTagStart.until(this.offset());
}
element.loc = element.loc.withEnd(this.offset());
appendChild(parent, b.element(element));
}
markTagAsSelfClosing(): void {
let tag = this.currentTag;
if (tag.type === 'StartTag') {
tag.selfClosing = true;
} else {
throw generateSyntaxError(
`Invalid end tag: closing tag must not be self-closing`,
this.source.spanFor({ start: tag.start.toJSON(), end: this.offset().toJSON() })
);
}
}
// Tags - name
appendToTagName(char: string): void {
let tag = this.currentTag;
tag.name += char;
if (tag.type === 'StartTag') {
let offset = this.offset();
if (tag.nameStart === null) {
assert(tag.nameEnd === null, 'nameStart and nameEnd must both be null');
View on GitHub (pinned to 26f97246a8)
Solutions
- Remove the trailing slash from the closing tag: </div/> → </div>.
- If the element was meant to be self-closing, put the slash on the start tag instead and delete the separate end tag: <img src="x.png" /> with no </img>.
- Re-check the surrounding lines for duplicated '/' characters left over from an edit.
Example fix
// before <div class="box"> content </div/> // after <div class="box"> content </div>
Defensive patterns
Strategy: validation
Validate before calling
// reject self-closing end tags before compiling
function assertNoSelfClosingEndTags(template) {
const bad = template.match(/<\s*\/\s*[A-Za-z][^>]*\/\s*>/g);
if (bad) throw new Error(`Closing tag must not be self-closing: ${bad.join(', ')}`);
}
assertNoSelfClosingEndTags(template); Prevention
- Put the '/' only on start tags (<img />), never on closing tags
- Grep templates for '</' followed by '/' as a quick check
- When generating templates programmatically, emit closing tags without trailing slashes
- Format templates with a formatter (prettier-plugin-ember-template-tag etc.) that normalizes tags
When it happens
Trigger: Writing a closing tag with a trailing slash: </div/>, </ul />, </:slot/>. The parser invokes markTagAsSelfClosing when it encounters '/' inside the tag; since tag.type is 'EndTag' the else branch throws.
Common situations: Typos where the self-closing slash meant for a start tag (<br/>) drifted onto the end tag; hand-editing templates and leaving </div/>; codemods or string concatenation producing malformed close tags.
Related errors
- Invalid end tag: closing tag must not have attributes
- Changing context using "../" is not supported in Glimmer
- Mixing '.' and '/' in paths is not supported in Glimmer; use
- '.' is not a supported path in Glimmer; check for a path wit
- Attempted to parse a path expression, but it was not valid.
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/2ade3f89b880a354.
Report an issue: GitHub.