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

Stray doctype

Error message

Stray doctype

What it means

process_token_in_foreign_content (crates/swc_html_parser/src/parser/mod.rs:712) flags a DOCTYPE token that arrives while the insertion point is inside an SVG/MathML subtree. The HTML5 spec says: parse error, ignore the token — nothing is appended to the tree, so this error is purely informational and parsing continues untouched.

Source

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

            // Insert the token's character.
            //
            // Set the frameset-ok flag to "not ok".
            Token::Character { .. } => {
                self.insert_character(token_and_info)?;

                self.frameset_ok = false;
            }
            // 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 one of: "b", "big", "blockquote", "body", "br",
            // "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4",
            // "h5", "h6", "head", "hr", "i", "img", "li", "listing", "menu", "meta", "nobr", "ol",
            // "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup", "table",
            // "tt", "u", "ul", "var"
            //
            // A start tag whose tag name is "font", if the token has any attributes named "color",
            // "face", or "size"
            //
            // An end tag whose tag name is "br", "p"
            //
            // Parse error.
            //
            // While the current node is not a MathML text integration point, an HTML integration
            // point, or an element in the HTML namespace, pop elements from the stack of open
            // elements.
            //

View on GitHub (pinned to 5176682b65)

Solutions

  1. Strip the XML/SVG doctype from the fragment before inlining it into HTML
  2. Keep exactly one doctype per document, emitted only by the outermost layout
  3. If you deliberately parse SVG-as-HTML fragments, filter ErrorKind::StrayDoctype from the error list

Example fix

<!-- before -->
<svg>
  <!DOCTYPE svg>
  <rect width="10" height="10"/>
</svg>

<!-- after -->
<svg>
  <rect width="10" height="10"/>
</svg>
Defensive patterns

Strategy: validation

Validate before calling

fn has_doctype_inside_foreign_content(html: &str) -> bool {
    let lower = html.to_ascii_lowercase();
    ["<svg", "<math"].iter().any(|root| {
        lower.match_indices(root).any(|(s, _)| {
            let close = format!("</{}", &root[1..]);
            let end = lower[s..].find(&close).map(|i| s + i).unwrap_or(lower.len());
            lower[s..end].contains("<!doctype")
        })
    })
}

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)?;

// Token is ignored by the spec recovery; document is complete.
for err in &errors {
    if matches!(err.kind(), ErrorKind::StrayDoctype) {
        log::warn!("doctype ignored inside foreign content");
    }
}

Prevention

When it happens

Trigger: A `<!DOCTYPE ...>` between `<svg>` (or `<math>`) and its closing tag, e.g. `<svg><!DOCTYPE svg><rect/></svg>` or `<math><mrow><!DOCTYPE math></mrow></math>`. Typically an XML/SVG file that legitimately carries its own doctype gets embedded as a fragment after the HTML parser already entered foreign content.

Common situations: Inlining standalone .svg files into HTML pages, server-side includes stitching doctype-bearing partials, CMS blocks that ship their own document skeleton, naive XHTML-to-HTML string conversion.

Related errors


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