swc-project/swc · warning · swc_html_parser::error::Error
Stray end tag "{tag_name}"
Error message
Stray end tag "{tag_name}" What it means
The 'any other end tag' path of process_token_in_foreign_content (crates/swc_html_parser/src/parser/mod.rs:1040) walks the stack of open elements looking for a name matching the end tag. If the name differs from the current node and the walk would pass the stack bottom (stack_idx == 0, only the root html element), the parser records StrayEndTag and silently drops the token — no element anywhere on the stack matches.
Source
Thrown at crates/swc_html_parser/src/parser/mod.rs:1040
// token, pop elements from the stack of open elements until node has been popped from
// the stack, and then return.
//
// Set node to the previous entry in the stack of open elements.
//
// If node is not an element in the HTML namespace, return to the step labeled loop.
//
// Otherwise, process the token according to the rules given in the section
// corresponding to the current insertion mode in HTML content.
Token::EndTag { tag_name, .. } => {
let mut node = self.open_elements_stack.items.last();
let mut stack_idx = self.open_elements_stack.items.len() - 1;
if let Some(node) = &node {
let node_tag_name = get_tag_name!(node);
if node_tag_name.to_ascii_lowercase() != **tag_name {
if stack_idx == 0 {
self.errors.push(Error::new(
token_and_info.span,
ErrorKind::StrayEndTag(tag_name.clone()),
));
} else {
self.errors.push(Error::new(
token_and_info.span,
ErrorKind::EndTagDidNotMatchCurrentOpenElement(
tag_name.clone(),
node_tag_name.into(),
),
));
}
}
}
loop {
if stack_idx == 0 || node.is_none() {
return Ok(());View on GitHub (pinned to 5176682b65)
Solutions
- Remove the orphan end tag, or open the element it was meant to close
- Emit each element's open and close tags from the same template block so they travel together
- Filter ErrorKind::StrayEndTag(_) when orphan closers are tolerated in your pipeline
Example fix
<!-- before --> <svg><rect/></svg></group> <!-- after --> <svg><rect/></svg>
Defensive patterns
Strategy: validation
Validate before calling
fn has_orphan_end_tags(html: &str) -> Vec<String> {
let lower = html.to_ascii_lowercase();
let mut stack: Vec<String> = Vec::new();
let mut orphan = Vec::new();
const VOID: &[&str] = &["br","img","meta","input","hr","link","source","area","base","col","embed","param","track","wbr"];
for (i, _) in lower.match_indices('<') {
let rest = &lower[i + 1..];
let (closing, body) = match rest.strip_prefix('/') {
Some(b) => (true, b),
None => (false, rest),
};
let name: String = body.chars().take_while(char::is_ascii_alphanumeric).collect();
if name.is_empty() { continue; }
if closing {
if !stack.contains(&name) {
orphan.push(format!("</{}> at byte {}", name, i));
} else if stack.last().as_deref() == Some(&name) {
stack.pop();
}
} else if !VOID.contains(&name.as_str()) {
stack.push(name);
}
}
orphan
} Try / catch
use swc_html_parser::error::ErrorKind;
let mut errors = Vec::new();
let doc = swc_html_parser::parse_file_as_document(&fm, config, &mut errors)?;
for err in &errors {
if let ErrorKind::StrayEndTag(tag) = err.kind() {
// Token ignored by recovery; tree unchanged at this point.
log::warn!("orphan end tag </{}>", tag);
}
} Prevention
- Never emit a closing tag without its matching opener in the same code path
- Run a tag-balance check over machine-generated HTML before parsing
- Treat orphan closers in logs as generator bugs, not parser noise
When it happens
Trigger: An end tag whose name matches nothing on the stack while inside <svg>/<math>, e.g. `<svg><rect/></svg></group>` or `<math><mi>x</mi></bar>`. Distinguished from error 726: here even the walk-to-bottom cannot find a match, so the closer is fully orphaned.
Common situations: Cut-and-paste markup leaving orphan closers, templating layers that always emit a closing tag for a section whose opening tag was conditionally skipped, string-concatenated fragments.
Related errors
- End tag "{end_tag_name}" did not match the name of the curre
- StrayEndTag
- Unexpected null character
- Stray doctype
- HTML start tag "{tag_name}" in a foreign namespace context
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/73254d7be761c8a3.
Report an issue: GitHub.