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).extras

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. 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).
  2. Use a constraints file (-c constraints.txt) or a lockfile (pip-compile, uv pip compile) to pin transitive versions.
  3. Reduce the number of extras requested and split the install into smaller groups.
  4. 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

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


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/a499fcd55c56fddb.json. Report an issue: GitHub.