swc-project/swc · warning · Error

Saw an end tag after "body" had been closed

Error message

Saw an end tag after "body" had been closed

What it means

An end tag other than </html> appeared in the 'after body' insertion mode. This is the spec's parse error reported as ErrorKind::EndTagAfterBody; the parser switches to 'in body' and reprocesses the token, so normal end-tag handling applies afterwards.

Source

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

                    _ => {
                        match token {
                            // Doctype handled above
                            // Comment handled above
                            // EOF handled above
                            Token::Character { .. } => {
                                self.errors.push(Error::new(
                                    token_and_info.span,
                                    ErrorKind::NonSpaceCharacterAfterBody,
                                ));
                            }
                            Token::StartTag { tag_name, .. } => {
                                self.errors.push(Error::new(
                                    token_and_info.span,
                                    ErrorKind::StrayStartTag(tag_name.clone()),
                                ));
                            }
                            Token::EndTag { .. } => {
                                self.errors.push(Error::new(
                                    token_and_info.span,
                                    ErrorKind::EndTagAfterBody,
                                ));
                            }
                            _ => {
                                unreachable!();
                            }
                        }

                        self.insertion_mode = InsertionMode::InBody;
                        self.process_token(token_and_info, None)?;
                    }
                }
            }
            // The "in frameset" insertion mode
            InsertionMode::InFrameset => {
                // When the user agent is to apply the rules for the "in frameset" insertion
                // mode, the user agent must handle the token as follows:

View on GitHub (pinned to 5176682b65)

Solutions

  1. Remove or relocate the stray closing tag so every end tag sits inside body.
  2. Re-balance the markup: ensure each open tag inside <body> is closed before </body>.
  3. Add an HTML validation step (validator.nu or html5ever-based linter) to CI for template sources.
  4. If tolerated, filter ErrorKind::EndTagAfterBody from the errors vec; the parser recovers.

Example fix

<!-- before -->
<body><div><p>x</div></p></body></div></html>

<!-- after -->
<body><div><p>x</p></div></body></html>
Defensive patterns

Strategy: validation

Validate before calling

// Any non-html end tag after the last </body> is a stray close.
fn has_end_tag_after_body(src: &str) -> bool {
    let lower = src.to_ascii_lowercase();
    match lower.rfind("</body>") {
        None => false,
        Some(i) => lower[i..].matches("</").count() > 1, // </body> itself + more
    }
}

Type guard

use swc_html_parser::error::{Error, ErrorKind};

fn is_end_tag_after_body(err: &Error) -> bool {
    matches!(err.kind(), ErrorKind::EndTagAfterBody)
}

Try / catch

let mut errors = Vec::new();
let doc = parse_file_as_document(&fm, config, &mut errors)?;
if errors.iter().any(is_end_tag_after_body) {
    log::warn!("end tag seen after body close; reprocessed in body");
}

Prevention

When it happens

Trigger: A stray closing tag after </body>, e.g. `</body></div>` or `</body></p>`. The 'anything else' arm of AfterBody mode matches Token::EndTag and pushes EndTagAfterBody.

Common situations: Templates whose macro/layout closes a wrapper element outside the body region; unbalanced markup where an outer container was closed early; copy-paste of page fragments that carry their own closing tags.

Related errors


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