swc-project/swc · error · TypeError

Assignment to constant variable.

Error message

Assignment to constant variable.

What it means

While handling an end tag in the Main phase, the parser compares the end tag's name with the tag name of the currently open element (top of open_elements_stack). If they differ, this mismatch error is recorded with the end tag's span. Parsing still recovers: the parser searches the open element stack for any element matching the end tag name and pops up to it if found, so the document remains usable.

Solutions

  1. Jump to the error span to see which end tag mismatches, then fix either that end tag or the corresponding unclosed inner element.
  2. Balance tags strictly: every start tag needs a matching end tag with the identical, case-sensitive name (including namespace prefix).
  3. If the document intentionally omits inner close tags, rewrite them as self-closing empty tags (`<child/>`) instead of relying on implied closes.
  4. Note the recovery rule: the parser pops up to the nearest matching name on the stack, so also check for cascading mismatch errors that all stem from one original typo.

Example fix

<!-- before: crossed/case-mismatched tags -->
<Config>
  <Item>value</item>
</config>

<!-- after -->
<Config>
  <Item>value</Item>
</Config>
Defensive patterns

Strategy: validation

Validate before calling

use swc_xml_parser::error::ErrorKind;

let mut errors = Vec::new();
let doc = swc_xml_parser::parse_file_as_document(&fm, config, &mut errors)?;
let mismatched = errors
    .iter()
    .any(|e| matches!(e.kind(), ErrorKind::OpeningAndEndingTagMismatch));
if mismatched {
    // document was recovered, but spans/end-tag spans may be inconsistent
    return Err("unbalanced tags".into());
}

Prevention

When it happens

Trigger: Any end tag that does not match the innermost open element: `<a><b></a></b>`-style crossings, `<Div></div>` case mismatches, namespace prefixes spelled differently in open/close tags, or an end tag for an element that was never opened. The comparison is on the raw token tag_name string, so prefix casing matters.

Common situations: Case mismatches from templating (e.g. React-style `<Div>` closed as `</div>`); refactoring that renames a tag in one place only; namespace-prefixed tags where the prefix differs between open and close; self-closing HTML habits (`<br>`) fed into an XML parser combined with mismatched closing tags.

Related errors


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

Appendix: source

Thrown at bindings/binding_nodejs_support_wasm/src/nodejs.rs:1148

        binding.namespace,
        export_property_access(&binding.export_name)
    )
}

fn export_property_access(export_name: &str) -> String {
    if is_identifier_name(export_name) {
        format!(".{export_name}")
    } else {
        format!("[{}]", json_string(export_name))
    }
}

fn import_meta_error_expression() -> String {
    "(() => { throw new SyntaxError(\"Cannot use import.meta outside a module\"); })()".to_string()
}

fn import_assignment_error_expression() -> String {
    "(() => { throw new TypeError(\"Assignment to constant variable.\"); })()".to_string()
}

fn is_identifier_name(value: &str) -> bool {
    let mut chars = value.chars();
    let Some(first) = chars.next() else {
        return false;
    };

    if !(first == '_' || first == '$' || first.is_ascii_alphabetic()) {
        return false;
    }

    chars.all(|ch| ch == '_' || ch == '$' || ch.is_ascii_alphanumeric())
}

enum ParseOutcome {
    Valid,
    Invalid(Vec<ParseError>),

View on GitHub (pinned to 5176682b65)