pytest-dev/pytest · error · UsageError

Missing required plugins

Error message

Missing required plugins: {}

What it means

Raised by _validate_plugins() (src/_pytest/config/__init__.py:1554-1557) when one or more entries in the `required_plugins` ini option cannot be matched against the active environment's installed plugin distributions. The check at config/__init__.py:1540-1552 parses each requirement with packaging.requirement.Requirement, then verifies the name exists in pluginmanager.list_plugin_distinfo() and that the installed version satisfies req.specifier. Failures accumulate and raise UsageError with the joined list.

Solutions

  1. pip install every plugin listed in the error message, honoring the version specifier
  2. Lower or relax the version pin in required_plugins if the newer version is acceptable
  3. Remove the entry from required_plugins if it is no longer actually used
  4. Verify the distribution name matches (use `pip show <name>` to confirm project_name)

Example fix

// before
# pyproject.toml
[tool.pytest.ini_options]
required_plugins = ['pytest-xdist>=4.0']
// after
pip install 'pytest-xdist>=4.0'
# or relax
required_plugins = ['pytest-xdist>=3.0']
Defensive patterns

Strategy: validation

Validate before calling

from packaging.requirements import Requirement
from importlib.metadata import distributions
dist_names = {d.metadata['Name'] for d in distributions()}
for req_str in required_plugins:
    r = Requirement(req_str)
    if r.name not in dist_names:
        print(f'Missing: {req_str}')

Type guard

def requirement_satisfied(req_str: str, installed: dict[str,str]) -> bool:
    from packaging.requirements import Requirement
    from packaging.version import Version
    r = Requirement(req_str)
    return r.name in installed and r.specifier.contains(Version(installed[r.name]), prereleases=True)

Try / catch

from _pytest.config import UsageError
try:
    pytest.main([])
except UsageError as e:
    if 'Missing required plugins' in str(e):
        import subprocess; subprocess.check_call(['pip','install',*missing])

Prevention

When it happens

Trigger: Setting `required_plugins = pytest-xdist>=3 pytest-cov` in pyproject.toml/pytest.ini when one is missing or below the pinned version. config/__init__.py:1543 catches InvalidRequirement (malformed spec) and config/__init__.py:1547-1552 handle missing-name and version-mismatch.

Common situations: CI runs without a plugin the project requires; bumping a version pin without reinstalling; typo in the plugin name; package installed under a different distribution name than the requirement (e.g. pytest-asyncio vs async-plugin aliases).

Related errors


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

Appendix: source

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

        plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info}

        missing_plugins = []
        for required_plugin in required_plugins:
            try:
                req = Requirement(required_plugin)
            except InvalidRequirement:
                missing_plugins.append(required_plugin)
                continue

            if req.name not in plugin_dist_info:
                missing_plugins.append(required_plugin)
            elif not req.specifier.contains(
                Version(plugin_dist_info[req.name]), prereleases=True
            ):
                missing_plugins.append(required_plugin)

        if missing_plugins:
            raise UsageError(
                "Missing required plugins: {}".format(", ".join(missing_plugins)),
            )

    def _warn_or_fail_if_strict(self, message: str) -> None:
        strict_config = self.getini("strict_config")
        if strict_config is None:
            strict_config = self.getini("strict")
        if strict_config:
            raise UsageError(message)

        self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3)

    def _get_unknown_ini_keys(self) -> set[str]:
        known_keys = self._parser._inidict.keys() | self._parser._ini_aliases.keys()
        return self._inicfg.keys() - known_keys

    def parse(self, args: list[str], addopts: bool = True) -> None:
        # Parse given cmdline arguments into this config object.

View on GitHub (pinned to 0d6fbdeffa)