{"record":{"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":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","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/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/mark/structures.py#L664-L699","documentation":"A ValueError raised by NodeKeywords.__delitem__ whenever code attempts `del node.keywords[key]`. The keywords mapping is a read-only union of a node's own markers and its parent's keywords; pytest deliberately makes deletion impossible to keep the keyword set consistent with the collected node tree. Any del operation, regardless of key, hits this guard.","triggerScenarios":"Calling `del item.keywords['smoke']`, `item.keywords.pop('x')` (if it routes through __delitem__), or any third-party code that mutates the keywords mapping. The method unconditionally raises.","commonSituations":"Plugins or conftest hooks that try to remove a keyword to influence selection; tests asserting on keywords and trying to clean up; porting code from a dict-like API.","solutions":["Do not delete keywords; instead, deselect the item via pytest_collection_modifyitems (config.hook.pytest_deselected).","If filtering by keyword, build a new list of selected items rather than mutating keywords.","Use a custom marker on the test rather than relying on dynamically removing keywords."],"exampleFix":"# before\ndel item.keywords['smoke']\n# after\ndef pytest_collection_modifyitems(items, config):\n    remaining = [i for i in items if 'smoke' not in i.keywords]\n    config.hook.pytest_deselected(items=[i for i in items if i not in remaining])\n    items[:] = remaining","handlingStrategy":"validation","validationCode":"def remove_keyword(items, key):\n    return [i for i in items if key not in i.keywords]\n# never call del item.keywords[key]","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat node.keywords as read-only.","Filter items via pytest_collection_modifyitems instead of mutating keywords.","Document this constraint for plugin authors."],"tags":["pytest","keywords","value-error","immutable","collection"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}