{"id":"0aa1a81270f1bbbe","repo":"pytest-dev/pytest","slug":"is-not-a-string-or-pytest-mark-marker","errorCode":null,"errorMessage":"is not a string or pytest.mark.* Marker","messagePattern":"is not a string or pytest\\.mark\\.\\* Marker","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/_pytest/nodes.py","lineNumber":323,"sourceCode":"        chain.reverse()\n        return chain\n\n    def add_marker(self, marker: str | MarkDecorator, append: bool = True) -> None:\n        \"\"\"Dynamically add a marker object to the node.\n\n        :param marker:\n            The marker.\n        :param append:\n            Whether to append the marker, or prepend it.\n        \"\"\"\n        from _pytest.mark import MARK_GEN\n\n        if isinstance(marker, MarkDecorator):\n            marker_ = marker\n        elif isinstance(marker, str):\n            marker_ = getattr(MARK_GEN, marker)\n        else:\n            raise ValueError(\"is not a string or pytest.mark.* Marker\")\n        self.keywords[marker_.name] = marker_\n        if append:\n            self.own_markers.append(marker_.mark)\n        else:\n            self.own_markers.insert(0, marker_.mark)\n\n    def iter_markers(self, name: str | None = None) -> Iterator[Mark]:\n        \"\"\"Iterate over all markers of the node.\n\n        :param name: If given, filter the results by the name attribute.\n        :returns: An iterator of the markers of the node.\n        \"\"\"\n        return (x[1] for x in self.iter_markers_with_node(name=name))\n\n    def iter_markers_with_node(\n        self, name: str | None = None\n    ) -> Iterator[tuple[Node, Mark]]:\n        \"\"\"Iterate over all markers of the node.","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/nodes.py#L305-L341","documentation":"Node.add_marker accepts only a string (marker name resolved via pytest.mark) or a MarkDecorator instance. Any other type (int, exception, custom object) raises ValueError because the marker cannot be resolved.","triggerScenarios":"node.add_marker(123), node.add_marker(some_object), or passing a Mark object (not a MarkDecorator) instead of the decorator form.","commonSituations":"Programmatic marker application where the value comes from untrusted data; confusion between pytest.mark.x (MarkDecorator) and a stored Mark.","solutions":["Pass a marker name string: node.add_marker('skip')","Pass a MarkDecorator: node.add_marker(pytest.mark.skip)"],"exampleFix":"// before\nnode.add_marker(my_marker_object)\n// after\nnode.add_marker(pytest.mark.skip)","handlingStrategy":"type-guard","validationCode":"from _pytest.mark import MarkDecorator\ndef safe_add_marker(node, marker):\n    if not (isinstance(marker, (str, MarkDecorator))):\n        raise TypeError('marker must be str or MarkDecorator')\n    node.add_marker(marker)","typeGuard":"from _pytest.mark import MarkDecorator\ndef is_valid_marker(m) -> bool:\n    return isinstance(m, (str, MarkDecorator))","tryCatchPattern":null,"preventionTips":["Prefer passing marker name strings when applying markers dynamically","Use pytest.mark.<name> to obtain a MarkDecorator rather than reaching for internal Mark objects"],"tags":["nodes","markers","pytest","type-check"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}