{"id":"cd34c7f1ea2364a8","repo":"pytest-dev/pytest","slug":"nodeid-or-parent-must-be-provided","errorCode":null,"errorMessage":"nodeid or parent must be provided","messagePattern":"nodeid or parent must be provided","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/nodes.py","lineNumber":200,"sourceCode":"        #: Filesystem path where this node was collected from.\n        self.path: pathlib.Path = path\n\n        # The explicit annotation is to avoid publicly exposing NodeKeywords.\n        #: Keywords/markers collected from all scopes.\n        self.keywords: MutableMapping[str, Any] = NodeKeywords(self)\n\n        #: The marker objects belonging to this node.\n        self.own_markers: list[Mark] = []\n\n        #: Allow adding of extra keywords to use for matching.\n        self.extra_keyword_matches: set[str] = set()\n\n        if nodeid is not None:\n            assert \"::()\" not in nodeid\n            self._nodeid = nodeid\n        else:\n            if not self.parent:\n                raise TypeError(\"nodeid or parent must be provided\")\n            self._nodeid = self.parent.nodeid + \"::\" + self.name\n\n        #: A place where plugins can store information on the node for their\n        #: own use.\n        self.stash: Stash = Stash()\n        # Deprecated alias. Was never public. Can be removed in a few releases.\n        self._store = self.stash\n\n    @classmethod\n    def from_parent(cls, parent: Node, **kw) -> Self:\n        \"\"\"Public constructor for Nodes.\n\n        This indirection got introduced in order to enable removing\n        the fragile logic from the node constructors.\n\n        Subclasses can use ``super().from_parent(...)`` when overriding the\n        construction.\n","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/nodes.py#L182-L218","documentation":"Node.__init__ builds the node's nodeid from parent.nodeid + '::' + name when no explicit nodeid is given. With neither a parent nor an explicit nodeid the id cannot be derived, so pytest raises TypeError.","triggerScenarios":"Constructing a root-level Node with no parent and no nodeid keyword, e.g. MyNode('name', config=cfg, session=s) but nodeid omitted.","commonSituations":"Plugin authors creating top-level nodes (e.g. a custom Session child) without passing nodeid; incomplete constructor migration.","solutions":["Pass a parent node so nodeid is derived: MyNode(name, parent=parent_node)","For a root node pass nodeid explicitly, e.g. MyNode('name', config=cfg, session=s, nodeid='name')"],"exampleFix":"// before\nnode = MyNode('root', config=cfg, session=s)\n// after\nnode = MyNode('root', config=cfg, session=s, nodeid='root')","handlingStrategy":"validation","validationCode":"def make_node(cls, name, *, parent=None, nodeid=None, **kw):\n    if parent is None and not nodeid:\n        raise TypeError(\"provide parent or nodeid\")\n    return cls(name, parent=parent, nodeid=nodeid, **kw)","typeGuard":"def has_parent_or_nodeid(parent, nodeid) -> bool:\n    return parent is not None or bool(nodeid)","tryCatchPattern":null,"preventionTips":["For root-level custom nodes always pass an explicit nodeid","Otherwise rely on a parent so nodeid is derived"],"tags":["nodes","plugin-api","pytest","nodeid"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}