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
- Jump to the error span to see which end tag mismatches, then fix either that end tag or the corresponding unclosed inner element.
- Balance tags strictly: every start tag needs a matching end tag with the identical, case-sensitive name (including namespace prefix).
- If the document intentionally omits inner close tags, rewrite them as self-closing empty tags (`<child/>`) instead of relying on implied closes.
- 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
- Treat tag names as case-sensitive identifiers; lint templates where casing is inconsistent (e.g. <Div> vs </div>).
- Use an editor/IDE XML language mode that highlights mismatched end tags as you type.
- Serialize rather than hand-edit when possible - serializers cannot emit crossed tags.
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
- attempted to private field on non-instance
- attempted to get private field on non-instance
- attempted to reassign private method
- attempted to use private field on non-instance
- Cannot use import.meta outside a module
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)