pytest-dev/pytest · error · ValueError

cannot delete key in keywords dict

Error message

cannot delete key in keywords dict

What it means

`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.

Source

Thrown at src/_pytest/mark/structures.py:682

    # Note: we could've avoided explicitly implementing some of the methods
    # below and use the collections.abc fallback, but that would be slow.

    def __contains__(self, key: object) -> bool:
        return key in self._markers or (
            self.parent is not None and key in self.parent.keywords
        )

    def update(  # type: ignore[override]
        self,
        other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (),
        **kwds: Any,
    ) -> None:
        self._markers.update(other)
        self._markers.update(kwds)

    def __delitem__(self, key: str) -> None:
        raise ValueError("cannot delete key in keywords dict")

    def __iter__(self) -> Iterator[str]:
        # Doesn't need to be fast.
        yield from self._markers
        if self.parent is not None:
            for keyword in self.parent.keywords:
                # self._marks and self.parent.keywords can have duplicates.
                if keyword not in self._markers:
                    yield keyword

    def __len__(self) -> int:
        # Doesn't need to be fast.
        return sum(1 for keyword in self)

    def __repr__(self) -> str:
        return f"<NodeKeywords for node {self.node}>"

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Do not mutate `keywords` directly; instead deselect the item or apply/remove marks.
  2. To change selection, filter `items` in your hook or use `-k`/`-m`.
  3. If you need custom data on a node, use `item.stash` or `item.user_properties`.

Example fix

# before
def pytest_collection_modifyitems(items):
    del items[0].keywords['xfail']
# after
def pytest_collection_modifyitems(items):
    items[:] = [i for i in items if 'xfail' not in i.keywords]
Defensive patterns

Strategy: validation

Validate before calling

def safe_filter_keywords(items, drop):
    return [i for i in items if drop not in i.keywords]  # never `del item.keywords[...]`

Prevention

When it happens

Trigger: Calling `del item.keywords['something']` in a `pytest_collection_modifyitems` hook or any plugin that manipulates a node's `keywords` mapping.

Common situations: Plugins/hooks trying to remove a keyword to suppress selection; assuming `keywords` behaves like a normal dict.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/42adf7853bcf6af7.json. Report an issue: GitHub.