swc-project/swc · warning · Error

End tag "br"

Error message

End tag "br"

What it means

An end tag `br` (`</br>`) was tokenized (parser/mod.rs:3730-3746). There is no such thing as a `br` end tag — `br` is a void element — so the spec says: parse error, drop the token's attributes, and act as if a `<br>` start tag was seen. The parser records `EndTagBr`, inserts a synthetic `<br>` element from a fake token, immediately pops it, and sets `frameset_ok = false`.

Source

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

                        self.insert_html_element(token_and_info)?;
                        self.frameset_ok = false;
                        self.insertion_mode = InsertionMode::InTable;
                        maybe_allow_self_closing!(is_self_closing, tag_name);
                    }
                    // An end tag whose tag name is "br"
                    //
                    // Parse error. Drop the attributes from the token, and act as described in the
                    // next entry; i.e. act as if this was a "br" start tag token with no
                    // attributes, rather than the end tag token that it actually is.
                    Token::EndTag {
                        tag_name,
                        is_self_closing,
                        ..
                    } if tag_name == "br" => {
                        let is_self_closing = *is_self_closing;

                        self.errors
                            .push(Error::new(token_and_info.span, ErrorKind::EndTagBr));

                        self.reconstruct_active_formatting_elements()?;
                        self.insert_html_element(
                            &self.create_fake_token_and_info("br", Some(token_and_info.span)),
                        )?;
                        self.open_elements_stack.pop();

                        if is_self_closing {
                            token_and_info.acknowledged = true;
                        }

                        self.frameset_ok = false;
                    }
                    // A start tag whose tag name is one of: "area", "br", "embed", "img", "keygen",
                    // "wbr"
                    //
                    // Reconstruct the active formatting elements, if any.
                    //

View on GitHub (pinned to 5176682b65)

Solutions

  1. Replace `</br>` with `<br>` (HTML) or `<br/>` (XHTML-style), both accepted by the parser
  2. Fix the generator that emits end tags for void elements (`br`, `img`, `input`, `hr`, `meta`, `link`)
  3. Add a lint/grep check for `</br>`, `</img>`, `</input>` in your template pipeline
  4. Note the recovery inserts a real `<br>`, so expect one extra line break in output snapshots

Example fix

<!-- before -->
<p>first line</br>second line</p>

<!-- after -->
<p>first line<br>second line</p>
Defensive patterns

Strategy: validation

Validate before calling

// Trivial pre-check: reject `</br>` (and other void end tags) before parsing
fn has_void_end_tags(src: &str) -> bool {
    ["br","img","input","hr","meta","link","area","base","col","embed","source","track","wbr"]
        .iter()
        .any(|t| src.contains(&format!("</{t}")))
}

Type guard

fn is_end_tag_br(e: &Error) -> bool {
    matches!(e.kind, ErrorKind::EndTagBr)
}

Try / catch

let doc = parser.parse_document()?;
let br_fixups = parser.take_errors().into_iter().filter(is_end_tag_br).count();
if br_fixups > 0 {
    // each `</br>` became a real <br> in the tree — verify rendering still fits
    log::warn!("{br_fixups} literal </br> coerced to <br>");
}

Prevention

When it happens

Trigger: Literal `</br>` anywhere in the input, e.g. `<p>a</br>b</p>`. Often a typo for the XHTML-style self-closing `<br/>`, whose slash ends up on the wrong side.

Common situations: Hand-typed XHTML habits (`<br/>` mistyped as `</br>`), find/replace accidents that moved slashes, and machine-generated markup from tools that blindly emit close tags for every element including void ones.

Related errors


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