pypa/pip · error · InvalidPyProjectBuildRequires

invalid-pyproject-build-system-requires

invalid-pyproject-build-system-requires

Error message

Can not process {package}

What it means

Raised by load_pyproject_toml (pyproject.py:89) as InvalidPyProjectBuildRequires (code: invalid-pyproject-build-system-requires) when [build-system].requires exists but is not a list of strings. PEP 518 requires requires to be a list of (PEP 508) requirement strings; any other type is rejected via the _is_list_of_str check.

Solutions

  1. Make requires a list of strings: requires = ["setuptools>=61"].
  2. Re-run after fixing the TOML; pip will re-parse on next build.
  3. Validate with 'tomllib.loads' / a TOML schema linter.

Example fix

# before (pyproject.toml)
[build-system]
requires = "setuptools>=61"
# after
[build-system]
requires = ["setuptools>=61"]
Defensive patterns

Strategy: validation

Validate before calling

from pip._internal.utils.compat import tomllib

def requires_is_list_of_str(pyproject_path: str) -> tuple[bool, str | None]:
    with open(pyproject_path, 'rb') as f:
        bs = tomllib.load(f).get('build-system')
    if bs is None:
        return True, None
    req = bs.get('requires')
    ok = isinstance(req, list) and all(isinstance(x, str) for x in req)
    return ok, None if ok else 'requires must be a list of strings'

Type guard

from typing import Any

def is_list_of_str(obj: Any) -> bool:
    return isinstance(obj, list) and all(isinstance(x, str) for x in obj)

Try / catch

from subprocess import run, CalledProcessError
try:
    run(["pip", "install", target], check=True)
except CalledProcessError:
    # Fix requires to a list of strings, then rebuild.
    run(["python", "-m", "build", target], check=True)

Prevention

When it happens

Trigger: [build-system].requires is set to a non-list (a string, dict, number) or a list containing non-string elements. e.g. requires = "setuptools" (a bare string instead of a list) or requires = [42].

Common situations: Author wrote a single string instead of a one-element list; YAML->TOML conversion mistakes; copy-paste from docs that dropped the brackets.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/d38a65d45be35425. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_internal/pyproject.py:90

        # traditional direct setup.py execution, and require wheel and
        # a version of setuptools that supports that backend.

        build_system = {
            "requires": ["setuptools>=40.8.0"],
            "build-backend": "setuptools.build_meta:__legacy__",
        }

    # Ensure that the build-system section in pyproject.toml conforms
    # to PEP 518.

    # Specifying the build-system table but not the requires key is invalid
    if "requires" not in build_system:
        raise MissingPyProjectBuildRequires(package=req_name)

    # Error out if requires is not a list of strings
    requires = build_system["requires"]
    if not _is_list_of_str(requires):
        raise InvalidPyProjectBuildRequires(
            package=req_name,
            reason="It is not a list of strings.",
        )

    # Each requirement must be valid as per PEP 508
    for requirement in requires:
        try:
            get_requirement(requirement)
        except InvalidRequirement as error:
            raise InvalidPyProjectBuildRequires(
                package=req_name,
                reason=f"It contains an invalid requirement: {requirement!r}",
            ) from error

    backend = build_system.get("build-backend")
    backend_path = build_system.get("backend-path", [])
    check: list[str] = []
    if backend is None:

View on GitHub (pinned to f399c37189)