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

  1. Deduplicate attribute maps before serializing the tag (decide first-wins or last-wins policy)
  2. Fix the source XML by deleting the duplicate attribute
  3. If a template merges defaults with overrides, merge into one map keyed by qualified name before emitting
  4. 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

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


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