swc-project/swc · warning · Error

StrayDoctype

StrayDoctype

Error message

Stray doctype

What it means

A DOCTYPE token arrived while the parser was in the 'in head' insertion mode (crates/swc_html_parser/src/parser/mod.rs:1702) — inside the head element. Spec: parse error, ignore the token. This is the most common StrayDoctype in practice: a duplicated doctype appearing after head has started.

Source

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

                    // Insert the character.
                    Token::Character {
                        value: '\x09' | '\x0A' | '\x0C' | '\x0D' | '\x20',
                        ..
                    } => {
                        self.insert_character(token_and_info)?;
                    }
                    // A comment token
                    //
                    // Insert a comment.
                    Token::Comment { .. } => {
                        self.insert_comment(token_and_info)?;
                    }
                    // A DOCTYPE token
                    //
                    // Parse error. Ignore the token.
                    Token::Doctype { .. } => {
                        self.errors
                            .push(Error::new(token_and_info.span, ErrorKind::StrayDoctype));
                    }
                    // A start tag whose tag name is "html"
                    //
                    // Process the token using the rules for the "in body" insertion mode.
                    Token::StartTag { tag_name, .. } if tag_name == "html" => {
                        self.process_token_using_rules(token_and_info, InsertionMode::InBody)?;
                    }
                    // A start tag whose tag name is one of: "base", "basefont", "bgsound", "link"
                    //
                    // Insert an HTML element for the token. Immediately pop the current node off
                    // the stack of open elements.
                    //
                    // Acknowledge the token's self-closing flag, if it is set.
                    Token::StartTag {
                        tag_name,
                        is_self_closing,
                        ..
                    } if matches!(&**tag_name, "base" | "basefont" | "bgsound" | "link") => {

View on GitHub (pinned to 5176682b65)

Solutions

  1. Delete the doctype from head-included partials; keep exactly one at the document top
  2. Audit includes/snippets for embedded document skeletons (doctype, html, head)
  3. Filter ErrorKind::StrayDoctype when duplicates are acceptable

Example fix

<!-- before -->
<head>
  <meta charset="utf-8">
  <!DOCTYPE html>
  <title>t</title>
</head>

<!-- after -->
<head>
  <meta charset="utf-8">
  <title>t</title>
</head>
Defensive patterns

Strategy: validation

Validate before calling

fn doctype_inside_head(html: &str) -> bool {
    let lower = html.to_ascii_lowercase();
    match (lower.find("<head"), lower.rfind("</head")) {
        (Some(open), Some(close)) => lower[open..close].contains("<!doctype"),
        (Some(open), None) => lower[open..].contains("<!doctype"),
        _ => false,
    }
}

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 matches!(err.kind(), ErrorKind::StrayDoctype) {
        // Ignored inside head; head content parsed normally.
        log::warn!("doctype inside <head> ignored");
    }
}

Prevention

When it happens

Trigger: `<!DOCTYPE html><html><head><meta charset="utf-8"><!DOCTYPE html>` or any doctype token whose preceding content already opened head. Comments and metadata before it do not close head, so the stray doctype is still processed by in-head rules.

Common situations: A doctype line inside a shared <head> include partial, meta-tag snippets that carry a full document skeleton, duplicate doctypes from wrapper + content concatenation.

Related errors


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