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

  1. Add the missing </template> for every <template> in the source HTML.
  2. If the HTML is generated, fix the generator/serializer so it always emits the closing template tag, even on early-return/error paths.
  3. Verify the input is not truncated (check file size, checksum, or stream completion) before parsing.
  4. 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

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


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