yewstack/yew · error
could not remove attribute
Error message
could not remove attribute
What it means
AttributeWriter::remove (packages/yew/src/dom_bundle/btag/attributes.rs:209) calls Element::remove_attribute during diffing when an attribute disappears from a VTag, and unwraps with .expect("could not remove attribute"). remove_attribute throws InvalidCharacterError for names that are not valid attribute names — the same rule as set_attribute. Since removal uses the previously-set name, this panic means an invalid name was stored in the attribute set earlier and only blows up when the attribute is later removed during reconciliation.
Source
Thrown at packages/yew/src/dom_bundle/btag/attributes.rs:209
#[cfg(feature = "hydration")]
fn hydrate_set(el: &Element, key: &str, value: &AttributeOrProperty) {
match value {
AttributeOrProperty::Attribute(value) => {
debug_assert_eq!(
el.get_attribute(key).as_deref(),
Some(value.as_ref()),
"attribute `{key}` does not match the server-rendered value during hydration",
);
}
AttributeOrProperty::Property(_) => Self::set(el, key, value),
}
}
fn remove(el: &Element, key: &str, old_value: &AttributeOrProperty) {
match old_value {
AttributeOrProperty::Attribute(_) => el
.remove_attribute(intern(key))
.expect("could not remove attribute"),
AttributeOrProperty::Property(_) => {
let key = JsValue::from_str(key);
js_sys::Reflect::set(el.as_ref(), &key, &JsValue::UNDEFINED)
.expect("could not remove property");
}
}
}
}
impl Apply for Attributes {
type Bundle = Self;
type Element = Element;
fn apply(self, _root: &BSubtree, el: &Element) -> Self {
#[expect(deprecated)]
match &self {
Self::Static(arr) => {
for (k, v) in arr.iter() {View on GitHub (pinned to 0e4a05472f)
Solutions
- Validate attribute names at the boundary where dynamic names are introduced (reject empty strings and space/quote/'<'/'>'/'/'/'=' characters) so they never reach the VTag
- Log attribute keys when inserting into Attributes to catch malformed names at creation time, not at removal time
- Where possible, use static html! attribute names and pass values, not names, dynamically
Example fix
// before
let attrs: Vec<(AttrValue, AttrValue)> = deserialize_from_config();
// after
fn is_valid_attr_name(name: &str) -> bool {
!name.is_empty() && !name.chars().any(|c| matches!(c, ' ' | '"' | '\'' | '<' | '>' | '/' | '=' ) || c.is_control())
}
let attrs: Vec<(AttrValue, AttrValue)> = deserialize_from_config()
.into_iter().filter(|(k, _)| is_valid_attr_name(k)).collect(); 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())
}
// filter at ingestion so bad names never enter the attribute set (and never reach removal)
let safe: Vec<_> = incoming_attrs.into_iter().filter(|(k, _)| is_valid_attr_name(k)).collect(); Type guard
fn safe_attr_name(name: &str) -> Option<&str> { is_valid_attr_name(name).then_some(name) } Prevention
- Validate attribute names once, at the point they are created — removal uses the stored name later
- Do not feed raw external/config data into attribute maps without filtering
- Log dropped names in debug builds to spot regressions in name construction
When it happens
Trigger: A dynamically built invalid attribute name was applied earlier (or reached the VTag some other way) and a subsequent render removes that attribute, invoking remove_attribute with the bad name.
Common situations: Spread/rest-props components passing through a malformed key that survives until the element re-renders without it; attribute maps loaded from external data (JSON config, CMS output) containing names with spaces or empty strings; keys assembled by string concatenation with empty segments.
Related errors
- invalid attribute key
- could not set property
- could not remove property
- can't create namespaced element for vtag
- unkeyed child in fully keyed list
AI-assisted analysis of yewstack/yew@0e4a05472f (2026-08-22).
Data as JSON: /api/errors/ecd7f18451c00c3d.
Report an issue: GitHub.