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
- Make requires a list of strings: requires = ["setuptools>=61"].
- Re-run after fixing the TOML; pip will re-parse on next build.
- 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
- Always write requires as a list of strings, even for a single dependency.
- Validate pyproject.toml structure in CI with a TOML/PEP 518 checker.
- Use a project template that emits correct build-system tables.
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
- missing-pyproject-build-system-requires
- Got invalid value for load_only - should be one of
- does not appear to be a Python project: neither 'setup.py'…
- An error occurred while writing to the configuration file
- Configuration file contains invalid
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)