swc-project/swc · warning · Error

Stray start tag "{tag_name}"

Error message

Stray start tag "{tag_name}"

What it means

While in the "in body" insertion mode, a start tag for `caption`, `col`, `colgroup`, `frame`, `head`, `tbody`, `td`, `tfoot`, `th`, `thead`, or `tr` arrived (parser/mod.rs:4197-4221). These tags only make sense inside a table/frameset context; in body content the spec says: parse error, ignore the token completely — no element is created and the content is dropped from the tree.

Source

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

                    //
                    // Parse error. Ignore the token.
                    Token::StartTag { tag_name, .. }
                        if matches!(
                            &**tag_name,
                            "caption"
                                | "col"
                                | "colgroup"
                                | "frame"
                                | "head"
                                | "tbody"
                                | "td"
                                | "tfoot"
                                | "th"
                                | "thead"
                                | "tr"
                        ) =>
                    {
                        self.errors.push(Error::new(
                            token_and_info.span,
                            ErrorKind::StrayStartTag(tag_name.clone()),
                        ));
                    }
                    // Any other start tag
                    //
                    // Reconstruct the active formatting elements, if any.
                    //
                    // Insert an HTML element for the token.
                    Token::StartTag {
                        is_self_closing,
                        tag_name,
                        ..
                    } => {
                        self.reconstruct_active_formatting_elements()?;
                        self.insert_html_element(token_and_info)?;
                        maybe_allow_self_closing!(is_self_closing, tag_name);
                    }

View on GitHub (pinned to 5176682b65)

Solutions

  1. Wrap the markup in a proper `<table>` (with `<tbody>`/`<tr>` as needed) so the tags land in table context
  2. If copying row/cell partials, always include the table scaffolding around them
  3. Replace `<frame>`-based layouts (obsolete) with iframes or CSS layout
  4. Note the token is IGNORED — unlike most parse errors the content disappears from the tree, so fix the input rather than tolerating it

Example fix

<!-- before -->
<div><tr><td>cell</td></tr></div>

<!-- after -->
<div><table><tbody><tr><td>cell</td></tr></tbody></table></div>
Defensive patterns

Strategy: validation

Validate before calling

// Detect table-structure tags that are not inside a <table> ancestor
fn table_tags_outside_table(src: &str) -> bool {
    let mut table = 0i32;
    for tag in html_tag_tokens(src) {
        match tag {
            Start(ref n) if n == "table" => table += 1,
            End(ref n) if n == "table" => table = table.saturating_sub(1),
            Start(ref n) if matches!(n.as_str(), "caption"|"col"|"colgroup"|"frame"|"head"|"tbody"|"td"|"tfoot"|"th"|"thead"|"tr") && table == 0 => return true,
            _ => {}
        }
    }
    false
}

Type guard

fn is_stray_table_start_tag(e: &Error) -> bool {
    matches!(
        e.kind,
        ErrorKind::StrayStartTag(ref t)
            if matches!(&**t, "caption"|"col"|"colgroup"|"frame"|"head"|"tbody"|"td"|"tfoot"|"th"|"thead"|"tr")
    )
}

Try / catch

let doc = parser.parse_document()?;
if parser.take_errors().into_iter().any(is_stray_table_start_tag) {
    // CRITICAL: the token was IGNORED — content is missing from the tree; reject input
    return Err(ContentRejected::TableMarkupOutsideTable);
}

Prevention

When it happens

Trigger: `<div><td>cell</td></div>`, `<body><tr><td>x`, `<span><thead>` — any table-structure tag encountered with no table context. Also `<frame>` after content when the document isn't a frameset.

Common situations: Table rows copied into non-table containers; templates where a row partial is reused outside its table; legacy frameset pages parsed as normal documents; email HTML built by string concat that drops the `<table>` wrapper.

Related errors


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