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

NoElementToCloseButEndTagSeen

NoElementToCloseButEndTagSeen

Error message

No "{tag_name}" element in scope but a "{tag_name}" end tag seen

What it means

Inside the adoption agency algorithm (used for formatting end tags like `</b>`, `</a>`, `</em>`), step 4 locates the formatting element from the list of active formatting elements on the stack of open elements. If it is not on the stack at all (crates/swc_html_parser/src/parser/mod.rs:7314), the parser reports `NoElementToCloseButEndTagSeen`, removes the element from the active formatting list, and ignores the token.

Source

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

                });

            if formatting_element.is_none() {
                self.any_other_end_tag_for_in_body_insertion_mode(token_and_info);

                return Ok(());
            }

            let formatting_element = formatting_element.unwrap();

            // 4.
            let formatting_element_stack_index = self
                .open_elements_stack
                .items
                .iter()
                .rposition(|n| is_same_node(n, &formatting_element.1));

            if formatting_element_stack_index.is_none() {
                self.errors.push(Error::new(
                    token_and_info.span,
                    ErrorKind::NoElementToCloseButEndTagSeen(subject),
                ));
                self.active_formatting_elements
                    .remove(&formatting_element.1);

                return Ok(());
            }

            // 5.
            if formatting_element_stack_index.is_some()
                && !self
                    .open_elements_stack
                    .has_node_in_scope(&formatting_element.1)
            {
                self.errors.push(Error::new(
                    token_and_info.span,
                    ErrorKind::NoElementToCloseButEndTagSeen(subject),

View on GitHub (pinned to 5176682b65)

Solutions

  1. Fix the source so formatting elements are closed in LIFO order where practical
  2. Re-serialize the document through a tolerant parser (parse then print) to normalize the lists
  3. If untrusted input, cap nesting/formatting depth before parsing
  4. Treat as recoverable: the parser drops the token and continues; verify the final tree

Example fix

<!-- before (mis-nested formatting) -->
<b><i>one</b>two</i>
<!-- after -->
<b><i>one</i></b><i>two</i>
Defensive patterns

Strategy: validation

Validate before calling

// Normalize before parsing: run the tolerant parser once and re-serialize
let normalized = {
    let mut parser = Parser::new(lexer);
    let doc = parser.parse_document()?;
    print_document(&doc)? // re-serialization resets active-formatting state
};

Try / catch

for err in parser.take_errors() {
    if matches!(err.kind, ErrorKind::NoElementToCloseButEndTagSeen(_)) {
        // end tag ignored; formatting entry removed from active list
    }
}

Prevention

When it happens

Trigger: A formatting element still referenced by the active formatting elements list but no longer present on the open elements stack — typically after table/text insertion-mode handling popped it — followed by its end tag. Example shape: `<b><li>x</b>` variants where earlier recovery left the lists inconsistent, then `</b>` arrives.

Common situations: Deeply mis-nested formatting tags produced by WYSIWYG editors; malformed HTML round-tripped through tolerant tools; inputs intentionally crafted to stress the adoption agency (fuzz corpora, legacy CMS output).

Related errors


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