pypa/pip · error · ResolutionTooDeepError
Dependency resolution exceeded maximum depth
Error message
Dependency resolution exceeded maximum depth
What it means
Raised at src/pip/_internal/resolution/resolvelib/resolver.py:114 when the vendored resolvelib emits ResolutionTooDeep. The resolver is invoked at lines 102-105 with max_rounds=200000; resolvelib raises ResolutionTooDeep once backtracking exceeds that round budget, and pip translates it to pip._internal.exceptions.ResolutionTooDeepError (exceptions.py:1001), whose message is exactly 'Dependency resolution exceeded maximum depth'. It is not a syntax error — it means the dependency graph is too large/under-constrained for the backtracking algorithm to settle within the round budget.
Source
Thrown at src/pip/_internal/resolution/resolvelib/resolver.py:114
resolver: RLResolver[Requirement, Candidate, str] = RLResolver(
provider,
reporter,
)
try:
limit_how_complex_resolution_can_be = 200000
result = self._result = resolver.resolve(
collected.requirements, max_rounds=limit_how_complex_resolution_can_be
)
except ResolutionImpossible as e:
error = self.factory.get_installation_error(
cast("ResolutionImpossible[Requirement, Candidate]", e),
collected.constraints,
)
raise error from e
except ResolutionTooDeep:
raise ResolutionTooDeepError from None
req_set = RequirementSet(check_supported_wheels=check_supported_wheels)
# process candidates with extras last to ensure their base equivalent is
# already in the req_set if appropriate.
# Python's sort is stable so using a binary key function keeps relative order
# within both subsets.
for candidate in sorted(
result.mapping.values(), key=lambda c: c.name != c.project_name
):
ireq = candidate.get_install_requirement()
if ireq is None:
if candidate.name != candidate.project_name:
# extend existing req's extras
with contextlib.suppress(KeyError):
req = req_set.get_requirement(candidate.project_name)
req_set.add_named_requirement(
install_req_extend_extras(
req, get_requirement(candidate.name).extrasView on GitHub (pinned to d7d0d0a394)
Solutions
- Add lower bounds (and upper bounds where safe) to constrain the search, e.g. 'package>=2.0,<3.0' instead of bare 'package' — this is pip's own recommended fix (exceptions.py:1013-1016).
- Use a constraints file (-c constraints.txt) or a lockfile (pip-compile, uv pip compile) to pin transitive versions.
- Reduce the number of extras requested and split the install into smaller groups.
- Upgrade pip to the latest release; resolver performance improves release over release.
Example fix
# before pip install big-framework # after echo 'big-framework>=2.0,<3.0' > constraints.txt pip install -c constraints.txt big-framework
Defensive patterns
Strategy: validation
Validate before calling
MAX_ROOT_REQS = 200 # heuristic guard before invoking the resolver
def is_likely_safe_to_resolve(root_reqs: list[str]) -> bool:
if len(root_reqs) > MAX_ROOT_REQS:
return False
# Reject bare, unconstrained names that force wide backtracking.
return all(('>' in r or '<' in r or '=' in r or '@' in r or '~' in r)
or r.startswith(('-c', '-r'))
for r in root_reqs) Try / catch
from pip._internal.exceptions import ResolutionTooDeepError
try:
req_set = resolver.resolve(root_reqs, check_supported_wheels=True)
except ResolutionTooDeepError:
raise SystemExit(
'Resolution exceeded 200000 rounds. Add lower/upper bounds or use '
'a constraints file (-c constraints.txt) to narrow the graph.'
) Prevention
- Pin transitive dependencies with a constraints file or a compiled lockfile (pip-compile / uv pip compile).
- Add lower bounds (and upper bounds where safe) to every requirement — this is pip's documented remedy.
- Request only the extras you actually use; each extra widens the graph.
- Keep pip current; the resolver's backtracking heuristics improve each release.
When it happens
Trigger: Running pip install/Resolver.resolve on a requirement set whose graph forces excessive backtracking: many packages with wide, overlapping version ranges, heavy use of extras, circular-ish constraints, or a single unconstrained meta-package that pulls hundreds of transitive deps. Also triggered by conflicting upper/lower bounds that make the solver explore most of the candidate space.
Common situations: Fresh 'pip install <big-framework>' with no lockfile, adding a package whose deps widen an existing tree, mixed pins from different teams, or an environment where some index serves an unusually large number of candidate versions per package.
Related errors
- Could not satisfy constraints for '{install_req.name}': inst
- Requested {ireq} has inconsistent name: expected {f_val!r},
- Requested {ireq} has inconsistent version: expected {f_val!r
- Requested {ireq} has invalid metadata: {error}
- Failed to build '{package_name}' when {failed_step.lower()}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/a499fcd55c56fddb.json.
Report an issue: GitHub.