swc-project/swc · warning · Error

"{tag_name}" end tag with "select" open

Error message

"{tag_name}" end tag with "select" open

What it means

In the "in select in table" mode, an end tag `caption`, `table`, `tbody`, `tfoot`, `thead`, `tr`, `td`, or `th` was seen while a select is open. HTML5 defines this as a parse error; if the named element is not in table scope the token is ignored, otherwise the parser pops the select, resets the insertion mode, and reprocesses. Note the error is reported before the scope check, so it fires in both paths.

Source

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

                    // If the stack of open elements does not have an element in table scope that is
                    // an HTML element with the same tag name as that of the token, then ignore the
                    // token.
                    //
                    // Otherwise:
                    //
                    // Pop elements from the stack of open elements until a select element has been
                    // popped from the stack.
                    //
                    // Reset the insertion mode appropriately.
                    //
                    // Reprocess the token.
                    Token::EndTag { tag_name, .. }
                        if matches!(
                            &**tag_name,
                            "caption" | "table" | "tbody" | "tfoot" | "thead" | "tr" | "td" | "th"
                        ) =>
                    {
                        self.errors.push(Error::new(
                            token_and_info.span,
                            ErrorKind::EndTagSeenWithSelectOpen(tag_name.clone()),
                        ));

                        if !self.open_elements_stack.has_in_table_scope(tag_name) {
                            // Ignore
                            return Ok(());
                        }

                        self.open_elements_stack
                            .pop_until_tag_name_popped(&["select"]);
                        self.reset_insertion_mode();
                        self.process_token(token_and_info, None)?;
                    }
                    // Anything else
                    //
                    // Process the token using the rules for the "in select" insertion mode.
                    _ => {

View on GitHub (pinned to 5176682b65)

Solutions

  1. Add the missing `</select>` before `</td>`/`</tr>`/`</table>`.
  2. Emit select widgets as complete atomic blocks (`<select>…</select>`) inside cells.
  3. Pre-validate: flag table end tags that appear while a `<select` is unclosed.
  4. Recovery pops the select and reprocesses the closer; log `EndTagSeenWithSelectOpen` from the `errors` vec.

Example fix

<!-- before: select not closed before </td> -->
<table><tr><td><select><option>a</option></td></tr></table>

<!-- after -->
<table><tr><td><select><option>a</option></select></td></tr></table>
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check: table end tags must not appear while a <select> is unclosed
fn table_end_tag_with_select_open(html: &str) -> Option<&'static str> {
    let lower = html.to_ascii_lowercase();
    let mut select_depth = 0usize;
    for (i, _) in lower.match_indices('<') {
        let rest = &lower[i..];
        if rest.starts_with("<select") {
            select_depth += 1;
        } else if rest.starts_with("</select") {
            select_depth = select_depth.saturating_sub(1);
        } else if select_depth > 0
            && ["</caption", "</table", "</tbody", "</tfoot", "</thead", "</tr", "</td", "</th"]
                .iter()
                .any(|t| rest.starts_with(t))
        {
            return Some(&lower[i + 2..].split([' ', '>', '/']).next().unwrap());
        }
    }
    None
}

Type guard

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

fn is_table_end_with_select_open(err: &Error) -> bool {
    matches!(err.kind(), ErrorKind::EndTagSeenWithSelectOpen(_))
}

Try / catch

let mut errors = Vec::new();
let doc = parse_file_as_document(&fm, config, &mut errors)?;
for e in errors.iter().filter(|e| matches!(e.kind(), ErrorKind::EndTagSeenWithSelectOpen(_))) {
    // select was popped (or tag ignored); tree recovered — report missing </select> upstream
}

Prevention

When it happens

Trigger: Closing a cell/row/table while a select in that cell is still open, e.g. `<table><tr><td><select><option>a</option></td></tr></table>` — `</td>` arrives with the select unclosed.

Common situations: Templates that forget `</select>` before closing the cell; editor auto-closing that drops the select closer; component output where the select's end tag is omitted by a broken conditional.

Related errors


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