Skip to content
Latchkey

pytest-xdist "worker ... crashed" / "Replacing crashed worker" in CI

With pytest -n (xdist), tests run in worker subprocesses. When a worker dies hard - a native segfault, an OOM kill, or a test calling os._exit - xdist reports the worker crashed and may replace it, often losing the test that caused it.

What this error means

The run prints [gw0] node down: Not properly terminated and replacing crashed worker gw0, or worker gw1 crashed while running. The failure can be flaky and hard to attribute because the offending test’s output is lost with the worker.

pytest output
[gw0] node down: Not properly terminated
replacing crashed worker gw0
INTERNALERROR> ... worker 'gw0' crashed while running
'tests/test_native.py::test_segfault'

Diagnose it: what did pytest actually collect?

Most pytest failures that only happen in CI are collection or import-path problems rather than test failures. The runner has a different working directory, a different sys.path, and usually no editable install, so a test module that imports your package locally may not resolve at all.

Terminal
# what would run, without running it
pytest --collect-only -q | tail -20

# where pytest thinks the root is (drives conftest and import mode)
pytest --collect-only 2>&1 | grep -i rootdir

# is the package importable at all from the runner cwd?
python -c "import yourpackage, sys; print(yourpackage.__file__)"
python -c "import sys; print(sys.path)"

Common causes

A native crash (segfault) in a C extension

A C/Rust extension or ctypes call segfaults the worker process. Python cannot catch it, so xdist sees the worker die.

Out-of-memory kill on a parallel worker

Running too many workers multiplies memory use; the OOM killer terminates one, and xdist reports it as crashed.

A test calling os._exit / hard-killing the process

Code under test (or a misbehaving fixture) calls os._exit or sends a fatal signal, terminating the worker without normal teardown.

How to fix it

Isolate the crash by running serially

Re-run without xdist to get a clean traceback and identify the test.

Terminal
pytest -p no:xdist tests/test_native.py -x
# or reduce parallelism
pytest -n 2

Address the root cause

  1. Segfault → pin/upgrade the native dependency, or run that test in isolation.
  2. OOM → lower -n (workers), or raise the runner memory; watch total = per-worker × workers.
  3. os._exit → fix the code/fixture so it raises or returns instead of exiting.

Make a zero-test run fail the build

Terminal
# fail explicitly when nothing is collected
pytest --strict-markers -q
test "${PIPESTATUS[0]}" -ne 5 || { echo "pytest collected no tests"; exit 1; }

How to prevent it

  • Size -n to the runner’s memory, not just its CPU count.
  • Keep native dependencies pinned and tested for stability.
  • Quarantine known-crashy native tests rather than letting them flake the suite.

Frequently asked questions

What causes pytest-xdist "worker ... crashed" / "Replacing crashed worker" in CI?
There are 3 common causes: a native crash (segfault) in a c extension, out-of-memory kill on a parallel worker, and a test calling os._exit / hard-killing the process. A C/Rust extension or ctypes call segfaults the worker process.
How do I fix pytest-xdist "worker ... crashed" / "Replacing crashed worker" in CI?
There are 2 fixes depending on which cause you have: isolate the crash by running serially and address the root cause. Work through them in order, since the first is the most common.
What does pytest-xdist "worker ... crashed" / "Replacing crashed worker" in CI actually mean?
The run prints [gw0] node down: Not properly terminated and replacing crashed worker gw0, or worker gw1 crashed while running.
How do I stop pytest-xdist "worker ... crashed" / "Replacing crashed worker" in CI happening again?
Size -n to the runner’s memory, not just its CPU count. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card