pytest exit codes 0-6 (tested)

pytest exits with 0 all passed, 1 tests failed, 2 interrupted (usually an import error during collection), 3 internal error, 4 usage error (bad option, path or config), 5 no tests collected, and in pytest 9.1+ 6 too many warnings.

pytest's exit status tells you what kind of problem stopped the run, which matters in CI and in editors like VS Code that only show "exit code N". Every block below was run in a fresh directory; the $ lines are the exact commands.

Quick table

CodeNameMeaning
0OKall collected tests passed
1TESTS_FAILEDat least one test failed (or errored with --continue-on-collection-errors)
2INTERRUPTEDcollection error, Ctrl+C, or pytest.exit()
3INTERNAL_ERRORpytest or a plugin/hook crashed (INTERNALERROR>)
4USAGE_ERRORbad command line or config: unknown option, missing path, bad -k
5NO_TESTS_COLLECTEDnothing matched
6MAX_WARNINGS_ERRORtests passed but --max-warnings was exceeded (new in pytest 9.1)

The codes as pytest defines them

$ python -c "import pytest; print(pytest.__version__); [print(int(c), c.name) for c in pytest.ExitCode]"
9.1.1
0 OK
1 TESTS_FAILED
2 INTERRUPTED
3 INTERNAL_ERROR
4 USAGE_ERROR
5 NO_TESTS_COLLECTED
6 MAX_WARNINGS_ERROR

Use pytest.ExitCode in scripts instead of bare numbers, e.g. if rc == pytest.ExitCode.NO_TESTS_COLLECTED. Code 6 exists only in pytest 9.1 and later.

Exit codes 0 and 1: passed and failed

$ cat test_a.py
def test_ok():
    assert 1
$ pytest -q; echo "exit code: $?"
.                                                                        [100%]
1 passed in 0.00s
exit code: 0
$ cat test_b.py
def test_bad():
    assert 1 == 2
$ pytest -q | tail -2; echo "exit code: ${PIPESTATUS[0]}"
FAILED test_b.py::test_bad - assert 1 == 2
1 failed in 0.01s
exit code: 1

Code 1 is the normal "some test failed" result. It is not a pytest problem; read the FAILED lines.

Exit code 2: errors during collection (and pytest.exit)

$ cat test_c.py
import nosuchmod

def test_x():
    pass
$ pytest -q | tail -4; echo "exit code: ${PIPESTATUS[0]}"
=========================== short test summary info ============================
ERROR test_c.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.08s
exit code: 2

An import error in a test file stops the whole run before any test executes: Interrupted: 1 error during collection, exit code 2. Fix the import (see pytest error collecting ModuleNotFoundError). To run the other files anyway:

$ ls
test_a.py
test_c.py
$ pytest -q --continue-on-collection-errors | tail -1; echo "exit code: ${PIPESTATUS[0]}"
1 passed, 1 error in 0.01s
exit code: 1

With --continue-on-collection-errors the good tests run and the code becomes 1, not 2. Code 2 is also what you get from Ctrl+C and from pytest.exit() without a returncode:

$ cat test_h.py
import pytest

def test_stop():
    pytest.exit("stopping early")
$ pytest -q | tail -2; echo "exit code: ${PIPESTATUS[0]}"
!!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: stopping early !!!!!!!!!!!!!!!!!!!!!
no tests ran in 0.17s
exit code: 2

Exit code 3: INTERNALERROR

$ cat conftest.py
def pytest_collection_modifyitems(items):
    raise RuntimeError("boom in a hook")
$ pytest -q | tail -3; echo "exit code: ${PIPESTATUS[0]}"
INTERNALERROR> RuntimeError: boom in a hook

no tests ran in 0.01s
exit code: 3

An exception inside pytest itself, a plugin, or a hook in your conftest.py prints INTERNALERROR> lines and exits with 3. The last INTERNALERROR lines name the file and line; often it is a plugin incompatible with your pytest version, so try pip install -U on the plugin or -p no:<plugin> to disable it.

Exit code 4: usage errors

$ pytest -q --bogus; echo "exit code: $?"
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --bogus
  inifile: None
  rootdir: /tmp/xc384/d

exit code: 4
$ pytest -q nosuchdir; echo "exit code: $?"

no tests ran in 0.00s
ERROR: file or directory not found: nosuchdir

exit code: 4
$ pytest -q -k "test_ok and"; echo "exit code: $?"

no tests ran in 0.00s
ERROR: Wrong expression passed to '-k': test_ok and: at column 12: expected not OR left parenthesis OR identifier; got end of input

exit code: 4

Code 4 means pytest refused to start: an unknown option, a path that does not exist, or a -k expression it cannot parse. Note that a missing path is code 4, not 5. The most common case in CI is an option in the config file whose plugin is not installed:

$ cat pytest.ini
[pytest]
addopts = --cov=app
$ pytest -q; echo "exit code: $?"
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --cov=app
  inifile: /tmp/xc384/d/pytest.ini
  rootdir: /tmp/xc384/d

exit code: 4

--cov comes from pytest-cov; when that is not installed in the environment that runs pytest, every run fails with code 4 before collecting anything. The inifile: line tells you which config file added it. Install the plugin in that environment, or remove the option.

Exit code 5: no tests collected

$ ls
notatest.py
$ pytest -q; echo "exit code: $?"

no tests ran in 0.00s
exit code: 5

No file matched test_*.py / *_test.py. Details and fixes: pytest no tests ran (exit code 5).

Exit code 6: too many warnings (pytest 9.1+)

$ cat test_w.py
import warnings

def test_w():
    warnings.warn("old api", DeprecationWarning)
$ pytest -q --max-warnings 0 | tail -2; echo "exit code: ${PIPESTATUS[0]}"
Tests pass, but maximum allowed warnings exceeded: 1 > 0
1 passed, 1 warning in 0.00s
exit code: 6

All tests passed but the warning count exceeded --max-warnings (or the max_warnings ini option), so the run exits with 6.

VS Code "pytest discovery failed with exit code 2" or 4

$ pytest --collect-only -q | tail -2; echo "exit code: ${PIPESTATUS[0]}"
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
no tests collected, 1 error in 0.08s
exit code: 2

Test discovery runs pytest in collect-only mode, so the same codes apply: 2 = a test file failed to import (missing module, syntax error, wrong interpreter), 4 = pytest could not start with the given arguments or config (an addopts option from a plugin not installed in the selected interpreter, a bad path in python.testing.pytestArgs). Run python -m pytest --collect-only -q in the terminal with the same interpreter VS Code uses to see the full message; the Python Test Log output panel shows it too.

Related: pytest error vs failure, pytest error: unrecognized arguments.

Frequently asked questions

What does pytest exit code 2 mean?

The run was interrupted: most often a test file raised an error while being imported (collection error), or Ctrl+C / pytest.exit() was used.

What does pytest exit code 4 mean?

Usage error: pytest did not start because of an unknown option (often from addopts with a plugin not installed), a path that does not exist, or an invalid -k expression.

Why does VS Code say pytest discovery failed with exit code 2?

A test file failed to import during collection. Run python -m pytest --collect-only -q with the same interpreter to see the error.

Written with AI assistance. Measured on 2026-10-23 with pytest 9.1.1 on Python 3.13.5 (Linux). Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python and pytest are free.