{"record":{"id":"ebbd63d2ef243721","repo":"swc-project/swc","slug":"select-start-tag-where-end-tag-expected","errorCode":null,"errorMessage":"\"select\" start tag where end tag expected","messagePattern":"\"select\" start tag where end tag expected","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"crates/swc_html_parser/src/parser/mod.rs","lineNumber":5844,"sourceCode":"                            self.update_end_tag_span(popped.as_ref(), token_and_info.span);\n                            self.reset_insertion_mode();\n                        }\n                    }\n                    // A start tag whose tag name is \"select\"\n                    //\n                    // Parse error.\n                    //\n                    // If the stack of open elements does not have a select element in select scope,\n                    // ignore the token. (fragment case)\n                    //\n                    // Otherwise:\n                    //\n                    // Pop elements from the stack of open elements until a select element has been\n                    // popped from the stack.\n                    //\n                    // Reset the insertion mode appropriately.\n                    Token::StartTag { tag_name, .. } if tag_name == \"select\" => {\n                        self.errors.push(Error::new(\n                            token_and_info.span,\n                            ErrorKind::StartSelectWhereEndSelectExpected,\n                        ));\n\n                        if !self.open_elements_stack.has_in_select_scope(\"select\") {\n                            // Ignore\n\n                            return Ok(());\n                        }\n\n                        self.open_elements_stack\n                            .pop_until_tag_name_popped(&[\"select\"]);\n                        self.reset_insertion_mode();\n                    }\n                    // A start tag whose tag name is one of: \"input\", \"keygen\", \"textarea\"\n                    //\n                    // Parse error.\n                    //","sourceCodeStart":5826,"sourceCodeEnd":5862,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_html_parser/src/parser/mod.rs#L5826-L5862","documentation":"A `<select>` start tag was seen while the parser is already in the \"in select\" insertion mode. Nested selects are invalid; HTML5 makes this a parse error, then pops the existing select, resets the insertion mode, and reprocesses — effectively the new select replaces the old one.","triggerScenarios":"`<select>` inside an open select, e.g. `<select><option>a<option><select><option>b</select>`; produced by injecting a full `<select>` widget into markup that already has one open.","commonSituations":"Client-side/templating code that injects select markup into an existing select's options; components that render a select inside an option list by mistake; copy-paste of select blocks into select templates.","solutions":["Replace the outer select's content instead of nesting: close the previous `</select>` before starting a new one.","When injecting dynamic options, inject `<option>` elements only — never a whole `<select>` into an open one.","Lint/guard generated markup: a depth counter over `<select`/`</select` catches nesting before parsing.","Recovery replaces the outer select (spec behavior) — verify the resulting tree matches what you want, and log `StartSelectWhereEndSelectExpected` from the `errors` vec."],"exampleFix":"<!-- before: nested select -->\n<select><option>a</option><select><option>b</option></select></select>\n\n<!-- after: sibling selects -->\n<select><option>a</option></select>\n<select><option>b</option></select>","handlingStrategy":"validation","validationCode":"// Pre-check: detect nested <select> before parsing\nfn has_nested_select(html: &str) -> bool {\n    let mut depth = 0usize;\n    let lower = html.to_ascii_lowercase();\n    for (i, _) in lower.match_indices('<') {\n        let rest = &lower[i..];\n        if rest.starts_with(\"<select\") {\n            depth += 1;\n            if depth > 1 {\n                return true;\n            }\n        } else if rest.starts_with(\"</select\") {\n            depth = depth.saturating_sub(1);\n        }\n    }\n    false\n}","typeGuard":"use swc_html_parser::error::{Error, ErrorKind};\n\nfn is_nested_select_error(err: &Error) -> bool {\n    matches!(err.kind(), ErrorKind::StartSelectWhereEndSelectExpected)\n}","tryCatchPattern":"let mut errors = Vec::new();\nlet doc = parse_file_as_document(&fm, config, &mut errors)?;\nif errors.iter().any(|e| matches!(e.kind(), ErrorKind::StartSelectWhereEndSelectExpected)) {\n    // recovery replaced the outer select with the inner one; verify that is intended\n}","preventionTips":["Inject only <option> markup into an open select, never a whole new <select>.","Wrap injection helpers with a nested-select depth check (see validationCode).","Close each select before rendering the next one in list widgets.","Remember recovery semantics: the inner select wins — assert on the resulting tree in tests."],"tags":["html","html-parser","select","nested-elements","parse-error","swc"],"backgroundTag":"nested-select-html","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}