{"id":"a499fcd55c56fddb","repo":"pypa/pip","slug":"dependency-resolution-exceeded-maximum-depth","errorCode":null,"errorMessage":"Dependency resolution exceeded maximum depth","messagePattern":"Dependency resolution exceeded maximum depth","errorType":"exception","errorClass":"ResolutionTooDeepError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/resolution/resolvelib/resolver.py","lineNumber":114,"sourceCode":"        resolver: RLResolver[Requirement, Candidate, str] = RLResolver(\n            provider,\n            reporter,\n        )\n\n        try:\n            limit_how_complex_resolution_can_be = 200000\n            result = self._result = resolver.resolve(\n                collected.requirements, max_rounds=limit_how_complex_resolution_can_be\n            )\n\n        except ResolutionImpossible as e:\n            error = self.factory.get_installation_error(\n                cast(\"ResolutionImpossible[Requirement, Candidate]\", e),\n                collected.constraints,\n            )\n            raise error from e\n        except ResolutionTooDeep:\n            raise ResolutionTooDeepError from None\n\n        req_set = RequirementSet(check_supported_wheels=check_supported_wheels)\n        # process candidates with extras last to ensure their base equivalent is\n        # already in the req_set if appropriate.\n        # Python's sort is stable so using a binary key function keeps relative order\n        # within both subsets.\n        for candidate in sorted(\n            result.mapping.values(), key=lambda c: c.name != c.project_name\n        ):\n            ireq = candidate.get_install_requirement()\n            if ireq is None:\n                if candidate.name != candidate.project_name:\n                    # extend existing req's extras\n                    with contextlib.suppress(KeyError):\n                        req = req_set.get_requirement(candidate.project_name)\n                        req_set.add_named_requirement(\n                            install_req_extend_extras(\n                                req, get_requirement(candidate.name).extras","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/resolution/resolvelib/resolver.py#L96-L132","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\npip install big-framework\n\n# after\necho 'big-framework>=2.0,<3.0' > constraints.txt\npip install -c constraints.txt big-framework","handlingStrategy":"validation","validationCode":"MAX_ROOT_REQS = 200  # heuristic guard before invoking the resolver\n\ndef is_likely_safe_to_resolve(root_reqs: list[str]) -> bool:\n    if len(root_reqs) > MAX_ROOT_REQS:\n        return False\n    # Reject bare, unconstrained names that force wide backtracking.\n    return all(('>' in r or '<' in r or '=' in r or '@' in r or '~' in r)\n               or r.startswith(('-c', '-r'))\n               for r in root_reqs)","typeGuard":null,"tryCatchPattern":"from pip._internal.exceptions import ResolutionTooDeepError\n\ntry:\n    req_set = resolver.resolve(root_reqs, check_supported_wheels=True)\nexcept ResolutionTooDeepError:\n    raise SystemExit(\n        'Resolution exceeded 200000 rounds. Add lower/upper bounds or use '\n        'a constraints file (-c constraints.txt) to narrow the graph.'\n    )","preventionTips":["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."],"tags":["pip","resolver","resolvelib","dependencies","backtracking","resolution"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}