pytest-dev/pytest · error · TypeError

nodeid or parent must be provided

Error message

nodeid or parent must be provided

What it means

Raised by Node.__init__ when nodeid is None and there is no parent to derive it from. At lines 198-202, if nodeid is given it is stored directly; otherwise the nodeid is computed as parent.nodeid + '::' + name. Without either source, the node cannot be uniquely identified in pytest's collection tree, so a TypeError is raised.

Solutions

  1. Provide a parent node so nodeid is derived automatically.
  2. Supply an explicit nodeid string when constructing standalone.
  3. Route construction through from_parent to inherit nodeid semantics.

Example fix

// before
node = MyItem(name='test_foo')
// after
node = MyItem.from_parent(parent=parent_node, name='test_foo')
Defensive patterns

Strategy: validation

Validate before calling

if nodeid is None and parent is None:
    raise TypeError('provide nodeid or parent')

Type guard

def _nodeid_resolvable(nodeid, parent) -> bool:
    return nodeid is not None or parent is not None

Prevention

When it happens

Trigger: Node(name='x') with nodeid=None and parent=None; constructing a node without a parent and without an explicit nodeid string.

Common situations: Plugin authors instantiating nodes directly rather than via from_parent; standalone node construction in tests that omit nodeid.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/cd34c7f1ea2364a8. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/nodes.py:200

        #: Filesystem path where this node was collected from.
        self.path: pathlib.Path = path

        # The explicit annotation is to avoid publicly exposing NodeKeywords.
        #: Keywords/markers collected from all scopes.
        self.keywords: MutableMapping[str, Any] = NodeKeywords(self)

        #: The marker objects belonging to this node.
        self.own_markers: list[Mark] = []

        #: Allow adding of extra keywords to use for matching.
        self.extra_keyword_matches: set[str] = set()

        if nodeid is not None:
            assert "::()" not in nodeid
            self._nodeid = nodeid
        else:
            if not self.parent:
                raise TypeError("nodeid or parent must be provided")
            self._nodeid = self.parent.nodeid + "::" + self.name

        #: A place where plugins can store information on the node for their
        #: own use.
        self.stash: Stash = Stash()
        # Deprecated alias. Was never public. Can be removed in a few releases.
        self._store = self.stash

    @classmethod
    def from_parent(cls, parent: Node, **kw) -> Self:
        """Public constructor for Nodes.

        This indirection got introduced in order to enable removing
        the fragile logic from the node constructors.

        Subclasses can use ``super().from_parent(...)`` when overriding the
        construction.

View on GitHub (pinned to 0d6fbdeffa)