yewstack/yew · error

invalid attribute key

Error message

invalid attribute key

What it means

AttributeWriter::set (packages/yew/src/dom_bundle/btag/attributes.rs:176) forwards the attribute name to Element::set_attribute and unwraps with .expect("invalid attribute key"). set_attribute throws InvalidCharacterError when the name is not a valid attribute name: empty, or containing space, quote, '<', '>', '/', '=', or control characters. Literal names from the html! macro are always valid, so this panic almost always comes from dynamically built attribute names (spread attributes, attribute maps) into which invalid characters leaked.

Source

Thrown at packages/yew/src/dom_bundle/btag/attributes.rs:176

                None => true,
            } {
                Self::set(el, k, new);
            }
        }

        // Remove missing
        for (k, old_value) in old.iter() {
            if !new.contains_key(k) {
                Self::remove(el, k, old_value);
            }
        }
    }

    fn set(el: &Element, key: &str, value: &AttributeOrProperty) {
        match value {
            AttributeOrProperty::Attribute(value) => el
                .set_attribute(intern(key), value)
                .expect("invalid attribute key"),
            AttributeOrProperty::Property(value) => {
                let key = JsValue::from_str(key);
                js_sys::Reflect::set(el.as_ref(), &key, value).expect("could not set property");
            }
        }
    }

    /// Applies a value during hydration.
    ///
    /// Hydration assumes the DOM already matches the server-rendered HTML, so
    /// attributes are left untouched: re-writing them would needlessly trigger
    /// side effects. In debug builds we only assert that the existing attribute
    /// matches the expected value. Properties are not reflected in the HTML, so
    /// they are always set.
    #[cfg(feature = "hydration")]
    fn hydrate_set(el: &Element, key: &str, value: &AttributeOrProperty) {
        match value {
            AttributeOrProperty::Attribute(value) => {

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Validate or sanitize attribute names before they enter the VTag attribute set: reject empty strings and characters ' " < > / = and control chars
  2. Fix the code that constructs the offending key (the panic is a symptom; find which name is invalid by logging the key before set)
  3. Restrict dynamic names to a known-safe allowlist (data-, aria-, or your fixed set)

Example fix

// before
let key = format!("{} label", user_part);
attrs.insert(key, "1".into());

// after
fn is_valid_attr_name(name: &str) -> bool {
    !name.is_empty() && !name.chars().any(|c| matches!(c, ' ' | '"' | '\'' | '<' | '>' | '/' | '=' ) || c.is_control())
}
assert!(is_valid_attr_name(&key));
attrs.insert(key, "1".into());
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_attr_name(name: &str) -> bool {
    !name.is_empty()
        && !name.chars().any(|c| matches!(c, ' ' | '"' | '\'' | '<' | '>' | '/' | '=') || c.is_control())
}

// apply before inserting dynamic attribute names
if !is_valid_attr_name(&key) {
    log::warn!("dropping invalid attribute name: {key:?}");
} else {
    attrs.insert(key.into(), value.into());
}

Type guard

fn safe_attr_name(name: &str) -> Option<&str> { is_valid_attr_name(name).then_some(name) }

Prevention

When it happens

Trigger: Inserting an attribute whose key is built at runtime and contains invalid characters, e.g. Attributes::insert(format!("{name} x"), ...) or an empty-string key; spreading props whose keys are derived from user input without validation.

Common situations: Generic 'rest props' spread components that pass through arbitrary keys; building data-* or aria-* names by string concatenation where a segment is empty or has whitespace; deserializing attribute maps from JSON/config that includes invalid names.

Related errors


AI-assisted analysis of yewstack/yew@0e4a05472f (2026-08-22). Data as JSON: /api/errors/06d93eea915a1bd5. Report an issue: GitHub.