pytest filterwarnings error: fail on warnings (tested)

Run pytest -W error or set filterwarnings = error in pytest.ini / pyproject.toml to turn every warning into a test failure. Add ignore:<message regex>:Category lines after error for warnings you accept; the last matching filter wins.

By default pytest only reports warnings in a summary and the run still passes. To make warnings fail tests, turn them into errors with -W error on the command line or filterwarnings = error in the config. The rules for matching specific warnings differ between the two, which is where most surprises come from. All blocks were run on pytest 9.1.1.

1. Fail on any warning from the command line: -W error

$ cat test_a.py
import warnings

def old():
    warnings.warn("old() is deprecated", DeprecationWarning)
    return 1

def test_old():
    assert old() == 1
$ pytest -q | tail -1
1 passed, 1 warning in 0.00s
$ pytest -q -W error | grep -E "^E |passed|failed"; echo "exit code: ${PIPESTATUS[0]}"
E       DeprecationWarning: old() is deprecated
1 failed in 0.01s
exit code: 1

The warning is raised as an exception inside the test, so the test FAILS (exit code 1) and the traceback points at the warnings.warn line.

2. Make it permanent: filterwarnings = error

$ cat pytest.ini
[pytest]
filterwarnings =
    error
$ pytest -q | grep -E "^E |passed|failed"
E       DeprecationWarning: old() is deprecated
1 failed in 0.01s

The same works in pyproject.toml under [tool.pytest.ini_options] as filterwarnings = ["error"].

3. Only some warnings: action:message:category:module

$ pytest -q -W error::DeprecationWarning | tail -1
1 failed in 0.01s
$ pytest -q -W "error:old:DeprecationWarning" | tail -1
1 failed in 0.01s
$ pytest -q -W "error:.*deprecated:DeprecationWarning" | tail -1
1 passed, 1 warning in 0.00s

On the command line the message part is a literal prefix of the warning text, not a regex: old matches "old() is deprecated", but .*deprecated matched nothing and the test passed with a warning. In the config file the message is a regular expression matched from the start of the text, so the same filter works there:

$ cat pytest.ini
[pytest]
filterwarnings =
    error:.*deprecated:DeprecationWarning
$ pytest -q | tail -1
1 failed in 0.01s

Because it is a regex in the config, parentheses and dots in the message must be escaped (old\(\), as in the next block).

4. error plus exceptions: later lines win

$ cat pytest.ini
[pytest]
filterwarnings =
    error
    ignore:old\(\) is deprecated:DeprecationWarning
$ pytest -q | tail -1
1 passed in 0.00s
$ cat pytest.ini
[pytest]
filterwarnings =
    ignore:old\(\) is deprecated:DeprecationWarning
    error
$ pytest -q | tail -1
1 failed in 0.01s
$ pytest -q -W "ignore::DeprecationWarning" | tail -1
1 passed in 0.00s

When several filters match, the last one in the list wins. Put error first and the ignore: lines for warnings you accept (often from third-party libraries) after it. -W options on the command line take precedence over the config file, as the last command shows.

5. "while parsing the following warning configuration" (exit code 4)

$ pytest -q -W error::FooWarning 2>&1 | grep -v "^ \|^$" | tail -2; echo "exit code: ${PIPESTATUS[0]}"
Traceback (most recent call last):
AttributeError: module 'builtins' has no attribute 'FooWarning'. Did you mean: 'Warning'?
exit code: 4

A category name that is not a built-in warning must be written with its full import path, e.g. error::mylib.MyWarning; a bare unknown name stops pytest with a usage error (exit code 4) before any test runs.

6. A warning at import time becomes a collection error

$ cat mylib/__init__.py
import warnings
warnings.warn("mylib config is old", UserWarning)
$ pytest -q -W error | tail -3; echo "exit code: ${PIPESTATUS[0]}"
ERROR test_e.py - UserWarning: mylib config is old
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.08s
exit code: 2

If a library warns while being imported, -W error turns that into an error collecting the test file and the run is interrupted (exit code 2). Add an ignore: filter for that specific message after error.

7. Per test: mark.filterwarnings, and pytest.warns to expect one

$ cat test_f.py
import warnings, pytest

def old():
    warnings.warn("old() is deprecated", DeprecationWarning)
    return 1

@pytest.mark.filterwarnings("error")
def test_strict():
    assert old() == 1

def test_relaxed():
    assert old() == 1

def test_expects_warning():
    with pytest.warns(DeprecationWarning, match="deprecated"):
        old()
$ pytest -q -rA | grep -E "^(PASSED|FAILED)"
PASSED test_f.py::test_relaxed
PASSED test_f.py::test_expects_warning
FAILED test_f.py::test_strict - DeprecationWarning: old() is deprecated

@pytest.mark.filterwarnings("error") makes only that test strict. To assert that code warns, use pytest.warns(Category, match=...); inside it the warning is captured instead of failing the test.

Related: pytest Failed: DID NOT RAISE, suppress pandas warnings.

Frequently asked questions

How do I make pytest fail on warnings?

Use pytest -W error, or add filterwarnings = error to the [pytest] section of pytest.ini (or filterwarnings = ["error"] in pyproject.toml).

Why does my filterwarnings message pattern not match?

With -W on the command line the message is a literal prefix; in the config file it is a regex matched from the start, so escape ( ) and . there.

How do I ignore one warning but error on the rest?

List error first and then ignore:message:Category; when several filters match, the last one wins.

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