{"id":"42adf7853bcf6af7","repo":"pytest-dev/pytest","slug":"cannot-delete-key-in-keywords-dict","errorCode":null,"errorMessage":"cannot delete key in keywords dict","messagePattern":"cannot delete key in keywords dict","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/_pytest/mark/structures.py","lineNumber":682,"sourceCode":"\n    # Note: we could've avoided explicitly implementing some of the methods\n    # below and use the collections.abc fallback, but that would be slow.\n\n    def __contains__(self, key: object) -> bool:\n        return key in self._markers or (\n            self.parent is not None and key in self.parent.keywords\n        )\n\n    def update(  # type: ignore[override]\n        self,\n        other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (),\n        **kwds: Any,\n    ) -> None:\n        self._markers.update(other)\n        self._markers.update(kwds)\n\n    def __delitem__(self, key: str) -> None:\n        raise ValueError(\"cannot delete key in keywords dict\")\n\n    def __iter__(self) -> Iterator[str]:\n        # Doesn't need to be fast.\n        yield from self._markers\n        if self.parent is not None:\n            for keyword in self.parent.keywords:\n                # self._marks and self.parent.keywords can have duplicates.\n                if keyword not in self._markers:\n                    yield keyword\n\n    def __len__(self) -> int:\n        # Doesn't need to be fast.\n        return sum(1 for keyword in self)\n\n    def __repr__(self) -> str:\n        return f\"<NodeKeywords for node {self.node}>\"\n","sourceCodeStart":664,"sourceCodeEnd":699,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/mark/structures.py#L664-L699","documentation":"`NodeKeywords` is the keyword mapping attached to every test node. Its `__delitem__` is implemented to unconditionally raise ValueError, so `del node.keywords[name]` is never allowed — node keywords are derived from marks/node identity and are not user-mutable.","triggerScenarios":"Calling `del item.keywords['something']` in a `pytest_collection_modifyitems` hook or any plugin that manipulates a node's `keywords` mapping.","commonSituations":"Plugins/hooks trying to remove a keyword to suppress selection; assuming `keywords` behaves like a normal dict.","solutions":["Do not mutate `keywords` directly; instead deselect the item or apply/remove marks.","To change selection, filter `items` in your hook or use `-k`/`-m`.","If you need custom data on a node, use `item.stash` or `item.user_properties`."],"exampleFix":"# before\ndef pytest_collection_modifyitems(items):\n    del items[0].keywords['xfail']\n# after\ndef pytest_collection_modifyitems(items):\n    items[:] = [i for i in items if 'xfail' not in i.keywords]","handlingStrategy":"validation","validationCode":"def safe_filter_keywords(items, drop):\n    return [i for i in items if drop not in i.keywords]  # never `del item.keywords[...]`","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat `node.keywords` as read-only.","Use marks, `item.stash`, or `user_properties` for mutable per-node data."],"tags":["pytest","keywords","immutable","hooks"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}