pypa/pip · error · InvalidPyProjectBuildRequires
invalid-pyproject-build-system-requires
invalid-pyproject-build-system-requires
Error message
Can not process {package} What it means
Raised as InvalidPyProjectBuildRequires when [build-system].requires exists in pyproject.toml but is not a list of strings (validated by _is_list_of_str at pyproject.py:18). PEP 518 requires `requires` to be an array of PEP 508 requirement strings. A non-list value (a single string, a number, a dict) or a list containing non-string elements is rejected.
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 d7d0d0a394)
Solutions
- Change `requires` to be a TOML array of strings: wrap the value in `[ ... ]` and quote each entry.
- Ensure every element is a quoted PEP 508 specifier string, e.g. `requires = ["setuptools>=61.0", "wheel"]`.
- Re-run pip install; the build-system table now passes _is_list_of_str.
Example fix
# before [build-system] requires = "setuptools" # after [build-system] requires = ["setuptools>=61.0"]
Defensive patterns
Strategy: validation
Validate before calling
import tomllib
def validate_requires_is_str_list(path: str = "pyproject.toml") -> None:
with open(path, "rb") as f:
data = tomllib.load(f)
requires = data.get("build-system", {}).get("requires")
if not (isinstance(requires, list) and all(isinstance(r, str) for r in requires)):
raise TypeError("[build-system].requires must be a list of strings")
validate_requires_is_str_list() Type guard
def is_list_of_str(obj) -> bool:
return isinstance(obj, list) and all(isinstance(i, str) for i in obj) Try / catch
null
Prevention
- Always wrap requires values in square brackets and quote each entry.
- Lint pyproject.toml with a tool that understands PEP 518 (e.g. validate-pyproject).
When it happens
Trigger: Setting `requires = "setuptools"` (a bare string instead of a list). Setting `requires = [42]` or `requires = [{name="setuptools"}]`. The check at pyproject.py:89 fails because either isinstance(obj, list) is False or any element is not a str.
Common situations: Authors unfamiliar with TOML/PEP 518 writing a scalar instead of an array. Misconfigured tooling that emits a malformed requires. Editing pyproject.toml by hand and dropping the square brackets.
Related errors
- missing-pyproject-build-system-requires
- {req_name} does not appear to be a Python project: neither '
- failed-build-dependency-install
- Cannot use '--only-dependencies' in combination with {confli
- When restricting platform and interpreter constraints using
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/d38a65d45be35425.json.
Report an issue: GitHub.