pypa/pip · error · InstallationError
Unnamed requirements are not allowed as constraints
Error message
Unnamed requirements are not allowed as constraints
What it means
InstallationError raised by check_invalid_constraint_type (called from collect_root_requirements) when a requirement marked as a constraint (-c file) has no name — e.g. a URL or a bare path. Constraints may only constrain a named project plus version specifier; an unnamed entry is meaningless as a constraint and the resolver rejects it. A deprecation warning precedes the failure.
Source
Thrown at src/pip/_internal/resolution/resolvelib/factory.py:553
else:
# require the base from the link
yield self.make_requirement_from_candidate(cand)
if ireq.extras:
# require the extras on top of the base candidate
yield self.make_requirement_from_candidate(
self._make_extras_candidate(cand, frozenset(ireq.extras))
)
def collect_root_requirements(
self, root_ireqs: list[InstallRequirement]
) -> CollectedRootRequirements:
collected = CollectedRootRequirements([], {}, {})
for i, ireq in enumerate(root_ireqs):
if ireq.constraint:
# Ensure we only accept valid constraints
problem = check_invalid_constraint_type(ireq)
if problem:
raise InstallationError(problem)
if not ireq.match_markers():
continue
assert ireq.name, "Constraint must be named"
name = canonicalize_name(ireq.name)
if name in collected.constraints:
collected.constraints[name] &= ireq
else:
collected.constraints[name] = Constraint.from_ireq(ireq)
else:
reqs = list(
self._make_requirements_from_install_req(
ireq,
requested_extras=(),
)
)
if not reqs:
continue
template = reqs[0]View on GitHub (pinned to d7d0d0a394)
Solutions
- Open the constraints file and remove or rename any unnamed/URL/path lines.
- Replace direct-URL constraints with a named specifier, e.g. 'package==1.2.3', and put the URL on the install side instead.
- Move any URL requirement out of the '-c' file into a regular '-r' requirements file.
- Re-run pip to confirm no unnamed constraint remains.
Example fix
# before — constraints.txt https://example.com/packages/foo-1.0.tar.gz # after foo==1.0
Defensive patterns
Strategy: validation
Validate before calling
def constraints_have_no_unnamed(path: str) -> bool:
bad = []
for line in open(path):
s = line.strip()
if not s or s.startswith('#') or s.startswith('-'):
continue
if '://' in s or s.endswith(('.whl', '.tar.gz', '.zip')) or '/' in s and '=' not in s.split('/')[0]:
bad.append(s)
if bad:
print(f'unnamed constraints: {bad}')
return False
return True Type guard
def is_named_constraint(line: str) -> bool:
s = line.strip()
if not s or s.startswith(('#', '-')):
return True
from pip._vendor.packaging.requirements import Requirement
try:
r = Requirement(s); return bool(r.name)
except Exception:
return False Try / catch
if not constraints_have_no_unnamed('constraints.txt'):
print('fix unnamed constraints before pip')
else:
run_pip(['install', '-c', 'constraints.txt', 'pkg']) Prevention
- Keep constraints files to 'name==version' lines only.
- Lint constraints files in pre-commit for URLs/paths.
- Generate constraints from a trusted lockfile tool that emits named specs.
When it happens
Trigger: A constraints file (passed via '-c constraints.txt') contains a line like 'https://example.com/pkg.tar.gz' or a local path with no project name, and pip is resolving with the new resolver. check_invalid_constraint_type returns the problem string and collect_root_requirements raises InstallationError.
Common situations: Migrating an old constraints file that listed direct URL pins; mixing install requirements and constraint entries; tooling that writes constraints from a lockfile containing direct references.
Related errors
- Editable requirements are not allowed as constraints
- Constraints cannot have extras
- Could not satisfy constraints for '{install_req.name}': inst
- Need exactly one file to operate upon (--user, --site, --glo
- Could not determine appropriate file.
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/7d4f7b6c5e466aad.json.
Report an issue: GitHub.