{"record":{"id":"c02f176606cef01e","repo":"yewstack/yew","slug":"could-not-remove-property","errorCode":null,"errorMessage":"could not remove property","messagePattern":"could not remove property","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/yew/src/dom_bundle/btag/attributes.rs","lineNumber":213,"sourceCode":"                debug_assert_eq!(\n                    el.get_attribute(key).as_deref(),\n                    Some(value.as_ref()),\n                    \"attribute `{key}` does not match the server-rendered value during hydration\",\n                );\n            }\n            AttributeOrProperty::Property(_) => Self::set(el, key, value),\n        }\n    }\n\n    fn remove(el: &Element, key: &str, old_value: &AttributeOrProperty) {\n        match old_value {\n            AttributeOrProperty::Attribute(_) => el\n                .remove_attribute(intern(key))\n                .expect(\"could not remove attribute\"),\n            AttributeOrProperty::Property(_) => {\n                let key = JsValue::from_str(key);\n                js_sys::Reflect::set(el.as_ref(), &key, &JsValue::UNDEFINED)\n                    .expect(\"could not remove property\");\n            }\n        }\n    }\n}\n\nimpl Apply for Attributes {\n    type Bundle = Self;\n    type Element = Element;\n\n    fn apply(self, _root: &BSubtree, el: &Element) -> Self {\n        #[expect(deprecated)]\n        match &self {\n            Self::Static(arr) => {\n                for (k, v) in arr.iter() {\n                    Self::set(el, k, v);\n                }\n            }\n            Self::Dynamic { keys, values } => {","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/yewstack/yew/blob/0e4a05472fac4e5fce1befe60fa4a1e43a36b6a3/packages/yew/src/dom_bundle/btag/attributes.rs#L195-L231","documentation":"To remove a DOM property during diffing, AttributeWriter::remove (packages/yew/src/dom_bundle/btag/attributes.rs:213) 'unsets' it with Reflect::set(el, key, JsValue::UNDEFINED) and unwraps with .expect(\"could not remove property\"). Reflect::set throws a TypeError when the property has no setter (getter-only accessors such as dataset or classList) or is non-writable, so removing such a property panics even though setting it may never have been attempted.","triggerScenarios":"A previous render stored a property whose DOM accessor is getter-only, and the next render drops that property so the removal path runs Reflect::set with undefined against a setter-less accessor; frozen elements likewise reject the write.","commonSituations":"Generic property-spread components forwarding arbitrary keys; transient properties (from a plugin or A/B tool) that disappear on the next render; elements sealed/frozen by tests or third-party scripts before re-render.","solutions":["Do not treat getter-only DOM properties (dataset, classList, isContentEditable, ...) as settable properties in Yew — use the dedicated APIs (classes!, attributes) instead","Filter dynamic property names through an allowlist of writable properties before they reach the VTag","Avoid freezing/sealing elements that Yew still needs to patch"],"exampleFix":"// before\n// property map may contain getter-only keys like \"classList\"\nfor (k, v) in dynamic_props { tag.add_property(k, v); }\n\n// after\nconst WRITABLE: &[&str] = &[\"value\", \"checked\", \"href\", \"innerHtml\"];\nfor (k, v) in dynamic_props {\n    if WRITABLE.contains(&k.as_str()) { tag.add_property(k, v); }\n}","handlingStrategy":"validation","validationCode":"// removal is Reflect::set(key, undefined) — only store properties that tolerate that write\nconst REMOVABLE_PROPS: &[&str] = &[\"value\", \"checked\", \"href\", \"selected\"];\nfn is_removable_prop(name: &str) -> bool { REMOVABLE_PROPS.contains(&name) }","typeGuard":"fn is_getter_only_dom_prop(name: &str) -> bool { matches!(name, \"dataset\" | \"classList\" | \"isContentEditable\") }","tryCatchPattern":null,"preventionTips":["Do not store getter-only DOM properties (dataset, classList) as VTag properties — they fail both set and unset","Filter dynamic property names through a writable allowlist","Never freeze/seal DOM nodes that Yew still reconciles"],"tags":["rust","wasm","yew","dom","properties","runtime-panic"],"backgroundTag":"dom-property-set-failed","analyzedSha":"0e4a05472fac4e5fce1befe60fa4a1e43a36b6a3","analyzedAt":"2026-08-22T21:16:31.212Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}