swc-project/swc · error · swc_xml_parser::error::Error
DuplicateAttribute
DuplicateAttribute
Error message
Duplicate attribute
What it means
The XML lexer collects a tag's attribute names into an FxHashSet while materializing them (crates/swc_xml_parser/src/lexer/mod.rs:820). A name already present in the set yields `DuplicateAttribute` on the second occurrence's span. XML 1.0 forbids duplicate attribute names on a single element (a well-formedness constraint — unlike HTML, where later duplicates are simply dropped); the token still carries all attributes, so downstream code decides the winner.
Source
Thrown at crates/swc_xml_parser/src/lexer/mod.rs:820
}
fn emit_tag_token(&mut self, kind: Option<TagKind>) {
if let Some(mut current_tag_token) = self.current_tag_token.take() {
if let Some(kind) = kind {
current_tag_token.kind = kind;
}
let mut already_seen: FxHashSet<Atom> = Default::default();
let attributes = current_tag_token
.attributes
.drain(..)
.map(|attribute| {
let name = Atom::from(attribute.name);
if already_seen.contains(&name) {
self.errors
.push(Error::new(attribute.span, ErrorKind::DuplicateAttribute));
}
already_seen.insert(name.clone());
AttributeToken {
span: attribute.span,
name,
raw_name: attribute.raw_name.map(Atom::from),
value: attribute.value.map(Atom::from),
raw_value: attribute.raw_value.map(Atom::from),
}
})
.collect();
match current_tag_token.kind {
TagKind::Start => {
let start_tag_token = Token::StartTag {
tag_name: current_tag_token.tag_name.into(),View on GitHub (pinned to 5176682b65)
Solutions
- Deduplicate attribute maps before serializing the tag (decide first-wins or last-wins policy)
- Fix the source XML by deleting the duplicate attribute
- If a template merges defaults with overrides, merge into one map keyed by qualified name before emitting
- Add xmllint or an equivalent well-formedness check to CI for XML assets
Example fix
<!-- before --> <item id="1" id="2"/> <!-- after --> <item id="1"/>
Defensive patterns
Strategy: validation
Validate before calling
// Deduplicate attributes before emitting XML (last-wins policy)
function serializeTag(name: string, attrs: Record<string, string>): string {
const seen = new Set<string>();
const parts: string[] = [];
for (const [k, v] of Object.entries(attrs).reverse()) {
const q = k.includes(':') ? k : k; // keep qualified names as-is
if (!seen.has(q)) { seen.add(q); parts.unshift(`${q}="${v}"`); }
}
return `<${name} ${parts.join(' ')}>`;
} Try / catch
let mut parser = swc_xml_parser::parser::Parser::new(lexer);
let doc = parser.parse_document()?; // recovers; attributes all retained in token
for err in parser.take_errors() {
if matches!(err.kind, ErrorKind::DuplicateAttribute) { /* reject or dedupe input */ }
} Prevention
- Merge attribute maps by qualified name before serialization, never concatenate
- Run xmllint (well-formedness) on XML assets in CI
- Decide a documented first-wins/last-wins policy so dedupe is deterministic
When it happens
Trigger: Parsing any XML via swc_xml_parser where one start tag repeats an attribute name: `<item id="1" id="2"/>`, or where default namespace/prefix serialization accidentally emits the same qualified name twice.
Common situations: Dynamically built XML that concatenates two attribute maps; templating engines merging default attributes with user overrides without deduplicating; hand-edited config files; SVG assets exported by tools that repeat attributes (e.g. fill or class).
Related errors
- Assignment to constant variable.
- MissingWhitespaceBeforeQuestionInProcessingInstruction
- UnexpectedTokenInStartPhase
- UnexpectedCharacter
- The requested module '{specifier}' does not provide an expor
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/5fe6ce27a9f941d1.
Report an issue: GitHub.