pytest 9.2: xdist_group tests run on different workers (--dist loadgroup)
pytest -n 4 --dist loadgroup -v
[gw1] PASSED tests/test_db.py::test_insert[0]
[gw3] PASSED tests/test_db.py::test_insert[1]
# expected on one worker, with the group in the ID: tests/test_db.py::test_insert[0]@db
Short answer: pytest 9.2.0 (27 October 2026) changed where a test's ID is stored, and pytest-xdist 3.8.0 still writes the loadgroup suffix to the old place. The @<group> suffix is lost, so tests marked xdist_group are spread over all workers, silently: everything still passes until tests that share a database, file or port collide. Pin pytest below 9.2, or keep 9.2 and add the small conftest.py shim below, until pytest-xdist releases its fix.
Why it happens
With --dist loadgroup, pytest-xdist marks grouped tests by appending @<group> to each test's node ID (item._nodeid = f"{item.nodeid}@{group}" in xdist/remote.py), and its scheduler sends every ID with the same suffix to one worker. pytest 9.2.0 moved node IDs to a structured NodeId stored in item._id, and the nodeid property now reads only that. Writing item._nodeid no longer changes the ID, so no test carries a group and the scheduler treats them like --dist load. pytest-xdist has merged a fix (PR #1388) but its latest release on PyPI is still 3.8.0. We reproduced it on 28 October 2026: on pytest 9.1.1 all six tests of one group ran on one worker in three runs out of three; on 9.2.0 they ran on three workers in every run.
Fix
- Check: run
pytest -n 2 --dist loadgroup -v. If tests marked@pytest.mark.xdist_groupdo not show@<group>at the end of their ID, you are affected (pytest 9.2.0 with pytest-xdist 3.8.0). - Quickest fix: pin pytest below 9.2 in your requirements, lock file or constraints, for example
pytest>=9.1,<9.2, and reinstall. - To stay on pytest 9.2, add this to the
conftest.pyat the root of your tests. It copies the suffixed ID pytest-xdist wrote into the field pytest 9.2 reads, and does nothing on older pytest or without xdist:# conftest.py: pytest 9.2 + pytest-xdist 3.8.0 --dist loadgroup workaround (remove after the next xdist release) import pytest @pytest.hookimpl(trylast=True) def pytest_collection_modifyitems(config, items): try: from _pytest.nodeid import coerce_node_id # pytest >= 9.2 only except ImportError: return for item in items: wanted = item.__dict__.get("_nodeid") if isinstance(wanted, str) and wanted != item.nodeid: item._id = coerce_node_id(wanted) - Run the check again: the grouped tests show
@<group>and run on one worker. - When a pytest-xdist release after 3.8.0 includes PR #1388, upgrade it and remove the pin or the shim.
Watch out
- Nothing fails at first: CI stays green while grouped tests run in parallel, so the first sign is often a flaky test or a corrupted shared fixture.
- The shim uses pytest internals (
_pytest.nodeid,item._id); keep it only until the pytest-xdist release. - Other plugins that read or write
item._nodeidare hit by the same change; pytest-benchmark #321 reports'Function' object has no attribute '_nodeid'(reported upstream, not tested by us).
Sources
- pytest changelog: 9.2.0 (27 Oct 2026)
- pytest-xdist 3.8.0 source: xdist/remote.py (sets item._nodeid for loadgroup)
- pytest-xdist PR #1388: Fix loadgroup with pytest main node IDs (merged, not released yet)
- PyPI: pytest-xdist (latest release)
- pytest-benchmark #321: 'Function' object has no attribute '_nodeid'