pytest-xdist workers collected different tests: Different tests were collected between gw0 and gw1. The difference is:
Every pytest-xdist worker collects the suite on its own, and the controller needs all of them to produce the same test IDs in the same order. When they disagree, no test runs. It often fails on some runs and passes on others, which makes it look flaky. The first worker in the message is the one whose collection reached the controller first, usually gw0, and every other worker is compared against it.
MINIMAL REPRO
Reproduce it
Run on 2026-10-23 with pytest 9.1.1, pytest-xdist 3.8.0, Python 3.11.14, macOS, arm64. The output below is what it printed.
import pytest
ROLES = {"admin", "editor", "viewer", "billing", "support"}
@pytest.mark.parametrize("role", ROLES)
def test_role_can_log_in(role):
assert rolepython -m pytest -q -n 4_____________________________ ERROR collecting gw1 _____________________________
Different tests were collected between gw0 and gw1. The difference is:
--- gw0
+++ gw1
@@ -1,5 +1,5 @@
test_users.py::test_role_can_log_in[admin]
-test_users.py::test_role_can_log_in[support]
+test_users.py::test_role_can_log_in[billing]
test_users.py::test_role_can_log_in[viewer]
test_users.py::test_role_can_log_in[editor]
-test_users.py::test_role_can_log_in[billing]
+test_users.py::test_role_can_log_in[support]
To see why this happens see 'Known limitations' in documentation for pytest-xdist
# The same block follows for gw0 against gw2 and gw0 against gw3, then:
3 errors in 0.38sRANKED DIAGNOSIS
What usually causes it
- 01
parametrize iterates a set
String hashes are salted with a random value per Python process, so a set of strings iterates in a different order in each worker. The diff shows the same IDs, reordered. All 34 unsorted runs of the repro on four workers failed at collection.
DIAGNOSE
If every line in the diff appears once with + and once with -, only the order differs. Look for set, frozenset or set operations feeding parametrize or a fixture's params.
FIX
Pass `sorted(...)` instead of the set. The pytest-xdist known-limitations page gives the same fix.
- 02
Parameters are generated at import time
uuid4(), random values without a fixed seed, or timestamps in parameters or IDs give every worker different test IDs, not just a different order. Sorting does not help: a sorted list of three uuid fragments still differed between gw0 and gw1.
DIAGNOSE
If the + and - lines carry different IDs, a value is generated per process.
FIX
Generate the data from a fixed seed, or give the cases explicit ids. With ids=["first", "second", "third"] the uuid repro collected cleanly, but each worker still saw different data, so a fixed seed is the better fix.
- 03
Collection reads state that changes while workers start
Parametrizing from a database query, an API call, or the files in a directory that a session fixture or another worker writes to means each worker can read a different answer at collection time.
DIAGNOSE
Find parametrize arguments or pytest_generate_tests hooks that do I/O, and check whether anything writes to that source during the run.
FIX
Build the case list before the run (a checked-in file or a fixed snapshot), or move the lookup into the test body.
VERIFIED FIX
The fix, and how it was checked
Give every worker the same ordered list.
@pytest.mark.parametrize("role", sorted(ROLES))
def test_role_can_log_in(role):
assert roleRe-run on 2026-10-23 with pytest 9.1.1 and pytest-xdist 3.8.0 on Python 3.11.14: after switching to sorted(ROLES), five runs with -n 4 each printed 5 passed. Putting the bare set back made the next five runs end in 3 errors each. In the 34 earlier runs whose messages we read, the base worker was gw0 every time, never gw1.
When this is not the cause
- A test failure you can retry
- No test ran. Rerunning the job only changes whether the hash order happens to agree, which is why it can look intermittent with fewer workers or values.
- Fixing it with PYTHONHASHSEED
- PYTHONHASHSEED=0 did make the set repro pass five times out of five, because every worker then hashes the same way. It hides the set rather than removing it, and it does nothing for the uuid case.
- A broken pytest-xdist install
- The error comes from the controller comparing worker collections, which is exactly what xdist is for. The same suite without -n cannot show it.
Prevent the repeat
- →Never feed a set or dict built from one into parametrize.
- →Seed any generated test data with a constant.
- →Run the suite with -n 2 in CI even when it is small, so collection drift fails early.
Run this against your evidence
The browser triage workflow uses the same stable pattern slug and does not upload your paste.