swc-project/swc · warning · Error
End of file seen and there were open elements
Error message
End of file seen and there were open elements
What it means
The HTML parser hit end-of-input while a <template> element was still on the stack of open elements, inside the 'in template' insertion mode. This is the WHATWG 'eof-with-unclosed-elements' parse error: the source ends before the matching </template>. The parser recovers deterministically (pops the template, clears active formatting elements to the last marker, resets insertion mode, reprocesses EOF), so a Document is still returned; the error only records the unterminated template.
Source
Thrown at crates/swc_html_parser/src/parser/mod.rs:6170
// been popped from the stack.
//
// Clear the list of active formatting elements up to the last marker.
//
// Pop the current template insertion mode off the stack of template insertion
// modes.
//
// Reset the insertion mode appropriately.
//
// Reprocess the token.
Token::Eof => {
if !self.open_elements_stack.contains_template_element() {
self.stopped = true;
} else {
self.update_end_tag_span(
self.open_elements_stack.items.last(),
token_and_info.span,
);
self.errors.push(Error::new(
token_and_info.span,
ErrorKind::EofWithUnclosedElements,
));
self.open_elements_stack
.pop_until_tag_name_popped(&["template"]);
self.active_formatting_elements.clear_to_last_marker();
self.template_insertion_mode_stack.pop();
self.reset_insertion_mode();
self.process_token(token_and_info, None)?;
}
}
}
}
// The "after body" insertion mode
InsertionMode::AfterBody => {
// When the user agent is to apply the rules for the "after body" insertion
// mode, the user agent must handle the token as follows:
match token {View on GitHub (pinned to 5176682b65)
Solutions
- Add the missing </template> for every <template> in the source HTML.
- If the HTML is generated, fix the generator/serializer so it always emits the closing template tag, even on early-return/error paths.
- Verify the input is not truncated (check file size, checksum, or stream completion) before parsing.
- If the input is third-party and the recovered tree is acceptable, filter ErrorKind::EofWithUnclosedElements out of the errors vec returned via parse_file_as_document's errors parameter and treat it as a warning.
Example fix
<!-- before --> <template id="row"><div>name</div> <script>/* ... */</script> <!-- after --> <template id="row"><div>name</div></template> <script>/* ... */</script>
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check template tag balance before parsing (cheap heuristic).
fn template_tags_balanced(src: &str) -> bool {
let lower = src.to_ascii_lowercase();
lower.matches("<template").count() == lower.matches("</template").count()
}
if !template_tags_balanced(&html_src) {
return Err("unterminated <template> element".into());
} Type guard
use swc_html_parser::error::{Error, ErrorKind};
fn is_eof_with_unclosed_elements(err: &Error) -> bool {
matches!(err.kind(), ErrorKind::EofWithUnclosedElements)
} Try / catch
// swc_html_parser never throws recoverable errors; it collects them.
let mut errors = Vec::new();
let doc = parse_file_as_document(&fm, config, &mut errors)?; // Err = fatal only
let fatal: Vec<_> = errors.iter()
.filter(|e| !matches!(e.kind(), swc_html_parser::error::ErrorKind::EofWithUnclosedElements))
.collect();
if !fatal.is_empty() { log::warn!("html parse errors: {:?}", fatal); } Prevention
- Always emit </template> in generated HTML, including on error/early-return paths.
- Validate input completeness (size/checksum) before parsing streamed or downloaded files.
- Run an HTML validator on templates in CI to catch unterminated elements.
- Treat errors from take_errors()/the errors vec as warnings by default; Err from parse_file_as_document is the only fatal signal.
When it happens
Trigger: Calling parse_file_as_document or parse_file_as_document_fragment on input that opens <template> without a matching </template> before EOF, e.g. `<body><template><div>row</div>` (no closing tag). The error fires on the EOF token in the 'in template' mode only when open_elements_stack.contains_template_element() is true.
Common situations: Server-side templating that conditionally skips the closing tag; minifiers that strip 'redundant' </template>; truncated files (partial upload, cut logs, interrupted stream); hand-written HTML template literals in Rust string concat pipelines.
Related errors
- attempted to ${action} private field on non-instance
- End of file seen without seeing a doctype first, expected "<
- UnclosedElements
- Non-space character after body
- Saw an end tag after "body" had been closed
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/a72a49709b73689b.
Report an issue: GitHub.