swc-project/swc · warning · swc_html_parser::error::Error

End tag "{end_tag_name}" did not match the name of the curre

Error message

End tag "{end_tag_name}" did not match the name of the current open element ("{current_element_tag_name}")

What it means

An end tag `</br>` or `</p>` was processed while the current node was a foreign (SVG/MathML) element (crates/swc_html_parser/src/parser/mod.rs:807). The parser reports EndTagDidNotMatchCurrentOpenElement with the innermost open element's name, runs pop_until_in_foreign(), and reprocesses the token with HTML rules. The HTML element named by the end tag was never open in the foreign subtree.

Source

Thrown at crates/swc_html_parser/src/parser/mod.rs:807

                tag_name,
                attributes,
                ..
            } if tag_name == "font"
                && attributes
                    .iter()
                    .any(|attribute| matches!(&*attribute.name, "color" | "face" | "size")) =>
            {
                self.errors.push(Error::new(
                    token_and_info.span,
                    ErrorKind::HtmlStartTagInForeignContext(tag_name.clone()),
                ));
                self.open_elements_stack.pop_until_in_foreign();
                self.process_token(token_and_info, None)?;
            }
            Token::EndTag { tag_name, .. } if matches!(&**tag_name, "br" | "p") => {
                let last = get_tag_name!(self.open_elements_stack.items.last().unwrap());

                self.errors.push(Error::new(
                    token_and_info.span,
                    ErrorKind::EndTagDidNotMatchCurrentOpenElement(tag_name.clone(), last.into()),
                ));
                self.open_elements_stack.pop_until_in_foreign();
                self.process_token(token_and_info, None)?;
            }
            // Any other start tag
            //
            // If the adjusted current node is an element in the MathML namespace, adjust MathML
            // attributes for the token. (This fixes the case of MathML attributes that are not all
            // lowercase.)
            //
            // If the adjusted current node is an element in the SVG namespace, and the token's tag
            // name is one of the ones in the first column of the following table, change the tag
            // name to the name given in the corresponding cell in the second column. (This fixes
            // the case of SVG elements that are not all lowercase.)
            //
            // Tag name	            Element name

View on GitHub (pinned to 5176682b65)

Solutions

  1. Balance the end tag: close <p> where it was opened, outside the foreign subtree
  2. Fix the unclosed <svg>/<math> so following end tags are handled by HTML rules
  3. Treat the error as informational when the auto-close-and-reprocess recovery is acceptable

Example fix

<!-- before -->
<svg><text>hi</p></svg>

<!-- after -->
<svg><text>hi</text></svg>
Defensive patterns

Strategy: validation

Validate before calling

const VOID: &[&str] = &["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"];

// Heuristic stack checker: flags end tags that do not match the innermost open tag.
fn find_mismatched_end_tags(html: &str) -> Vec<String> {
    let lower = html.to_ascii_lowercase();
    let mut stack: Vec<String> = Vec::new();
    let mut bad = Vec::new();
    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 {
            match stack.last() {
                Some(open) if open == &name => { stack.pop(); }
                _ => bad.push(format!("</{}> at byte {}", name, i)),
            }
        } else if !VOID.contains(&name.as_str()) {
            stack.push(name);
        }
    }
    bad
}

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::EndTagDidNotMatchCurrentOpenElement(end, current) = err.kind() {
        // Foreign content was force-closed so </p> could be reprocessed as HTML
        log::warn!("</{}> saw open <{}>", end, current);
    }
}

Prevention

When it happens

Trigger: `</p>` or `</br>` inside an <svg>/<math> subtree, e.g. `<svg><text>hi</p></svg>`. Frequently the tail of a missing foreign-element boundary: after an unclosed <svg>/<math>, subsequent end tags are dispatched to foreign content until a breakout token resets the mode.

Common situations: Templates that conditionally emit the closing </svg> but always emit a trailing </p>, minified or machine-mangled markup, hand-edited SVG islands in HTML pages.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/e61be4da8352ff4f. Report an issue: GitHub.