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
- Replace `</br>` with `<br>` (HTML) or `<br/>` (XHTML-style), both accepted by the parser
- Fix the generator that emits end tags for void elements (`br`, `img`, `input`, `hr`, `meta`, `link`)
- Add a lint/grep check for `</br>`, `</img>`, `</input>` in your template pipeline
- 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
- Write <br> or <br/>, never </br>
- Fix generators that emit end tags for void elements
- Add a grep/CI check for `</br>`, `</img>`, `</input>` in template output
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
- Saw a start tag "image", "img" element is outdated
- failed to parse input as document fragment
- failed to parse input as document
- failed to parse input as document fragment
- failed to parse input as document
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/795ad2daa10f1548.
Report an issue: GitHub.