pytest Exit Codes in CI - What 0–5 Mean and How to Handle Them
pytest’s exit code tells your pipeline exactly what happened. Knowing the five codes turns a red CI step into a precise diagnosis instead of a guess.
What this error means
A pytest step fails and CI just shows a non-zero exit. The number is meaningful: each code maps to a distinct outcome, from real test failures to a usage error or an empty collection.
# pytest exit codes
0 All tests passed
1 Some tests failed
2 Test execution was interrupted (e.g. KeyboardInterrupt)
3 Internal error inside pytest
4 pytest usage/command-line error
5 No tests were collectedCommon causes
Different codes mean different problems
Exit 1 is genuine test failures; exit 5 is "nothing collected"; exit 4 is a bad command line; exit 2/3 are interruption or an internal pytest error. Treating them all as "tests failed" hides the real cause.
Wrappers can mask the code
Running pytest through a shell pipeline, make, or && chains can swallow or transform the exit code, so CI reports a confusing status.
How to fix it
Inspect the exit code directly
pytest; echo "pytest exit: $?"Handle codes intentionally
- Exit 1 → read the failures report and fix the failing tests.
- Exit 5 → fix collection/discovery (see the "no tests ran" guide).
- Exit 4 → fix the pytest command line or config (bad option/arg).
- Exit 2/3 → look for a crash, timeout, or interruption, not assertion failures.
How to prevent it
- Don’t mask pytest’s exit code behind shell pipelines.
- Distinguish exit 5 (no tests) from exit 1 (failures) in CI logic.
- Keep test invocation simple so the exit code reaches the runner unaltered.