pytest-dev/pytest · error · UsageError

plugin cannot be disabled

Error message

plugin {name} cannot be disabled

What it means

Raised by consider_pluginarg() (src/_pytest/config/__init__.py:847) when a `-p no:<name>` argument names a plugin in essential_plugins (config/__init__.py:313-319: mark, main, runner, fixtures, helpconfig). These plugins are mandatory for pytest to function, so disabling them is rejected with UsageError before set_blocked() is called.

Solutions

  1. Remove the `-p no:<essential>` argument from your CLI, PYTEST_ADDOPTS, or addopts
  2. If you need to disable some behavior of these plugins, find the specific ini option (e.g. use -p no:cacheprovider instead of disabling something essential)
  3. Replace the goal: for marker filtering use --markers/-m filters rather than disabling the mark plugin

Example fix

// before
pytest -p no:fixtures tests/
// after
pytest tests/
Defensive patterns

Strategy: validation

Validate before calling

from _pytest.config import essential_plugins
name = arg[3:]  # strip 'no:'
if name in essential_plugins:
    raise ValueError(f'Cannot disable essential plugin: {name}')

Type guard

from _pytest.config import essential_plugins
def is_disableable(name: str) -> bool:
    return name not in essential_plugins

Try / catch

from _pytest.config import UsageError
try:
    pytest.main(['-p', f'no:{name}'])
except UsageError as e:
    print(e)

Prevention

When it happens

Trigger: Invoking `pytest -p no:mark`, `-p no:main`, `-p no:runner`, `-p no:fixtures`, or `-p no:helpconfig`. The check at config/__init__.py:850-851 matches the stripped name against the essential_plugins tuple.

Common situations: Trying to shave startup time by disabling builtins; misreading docs that suggest `-p no:X` works for every plugin; copy-pasting a `-p no:...` block from a CI config.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/2779d1008d3e3d88. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/config/__init__.py:849

                        parg = args[i]
                    except IndexError:
                        return
                    i += 1
                elif opt.startswith("-p"):
                    parg = opt[2:]
                else:
                    continue
                parg = parg.strip()
                if exclude_only and not parg.startswith("no:"):
                    continue
                self.consider_pluginarg(parg)

    def consider_pluginarg(self, arg: str) -> None:
        """:meta private:"""
        if arg.startswith("no:"):
            name = arg[3:]
            if name in essential_plugins:
                raise UsageError(f"plugin {name} cannot be disabled")

            if name.endswith("conftest.py"):
                raise UsageError(
                    f"Blocking conftest files using -p is not supported: -p no:{name}\n"
                    "conftest.py files are not plugins and cannot be disabled via -p.\n"
                )

            # PR #4304: remove stepwise if cacheprovider is blocked.
            if name == "cacheprovider":
                self.set_blocked("stepwise")
                self.set_blocked("pytest_stepwise")

            self.set_blocked(name)
            if not name.startswith("pytest_"):
                self.set_blocked("pytest_" + name)
        else:
            name = arg
            # Unblock the plugin.

View on GitHub (pinned to 0d6fbdeffa)