How to Debug uv Dependency Resolution Failures
When uv lock or uv add prints No solution found when resolving dependencies, the resolver found two requirements that no single version can satisfy. The error names both sides of the conflict, and that message is the fastest path to a fix.
Prerequisites
- uv installed
- A uv project with a
pyproject.toml
Read the error message
uv prints a derivation chain that traces the conflict from your project’s requirements to the incompatible ranges:
$ uv add 'urllib3>=2' 'botocore==1.29.0'
× No solution found when resolving dependencies:
╰─▶ Because botocore==1.29.0 depends on urllib3>=1.25.4,<1.27 and your
project depends on botocore==1.29.0, we can conclude that your project
depends on urllib3>=1.25.4,<1.27.
And because your project depends on urllib3>=2, we can conclude that
your project's requirements are unsatisfiable.
Read it bottom-up. The last sentence states the contradiction: the project asks for urllib3>=2, but botocore==1.29.0 caps it at <1.27. No version of urllib3 satisfies both.
Trace the dependency tree
uv tree shows the full resolved graph. When a package appears in the error, run the inverted view to see everything that pulls it in. If resolution failed before any lockfile was written, remove the conflicting addition, lock the existing dependencies first, then inspect the tree.
$ uv tree --invert --package urllib3
urllib3 v2.7.0
├── botocore v1.29.0
│ └── uv-override-test v0.1.0
└── uv-override-test v0.1.0
The output names every path from urllib3 back to the project root. Two paths requesting incompatible ranges are the conflict.
Preview changes without committing
uv lock --dry-run resolves dependencies and reports what would change without writing the lockfileA file that records the exact version of every installed package, so everyone working on the project gets identical installs.
:
$ uv lock --upgrade-package requests --dry-run
Resolved 6 packages in 1.64s
Update requests v2.32.3 -> v2.34.2
Combine --dry-run with --resolution lowest to check whether the declared lower bounds still resolve. Library authors use this to verify that a user on the oldest supported versions gets a working install:
$ uv lock --resolution lowest --dry-run
Resolved 6 packages in 2.60s
Update certifi v2026.7.22 -> v2023.5.7
Update charset-normalizer v3.4.9 -> v2.0.0
Update idna v3.18 -> v2.5
Update urllib3 v2.7.0 -> v1.26.0
Fix the conflict with an override
When a transitive dependencyA package your dependency depends on. When you install requests, its own dependencies (urllib3, certifi, etc.) are transitive dependencies.
declares an incorrect upper bound, override it. Add override-dependencies under [tool.uv] in pyproject.toml to replace that package’s declared range:
[tool.uv]
override-dependencies = [
"urllib3>=1.25.4",
]This tells the resolver to ignore botocore’s urllib3<1.27 ceiling and accept any urllib3>=1.25.4. The override applies globally, so verify the result with uv tree to confirm the resolved version works.
Warning
Overrides bypass metadata that exists for a reason. Test the combination before shipping. If botocore genuinely breaks with urllib3>=2, the override hides the breakage instead of fixing it.
Narrow versions with a constraint
When the goal is to restrict rather than loosen, use constraint-dependencies. Constraints reduce the acceptable range without adding the package as a direct dependency:
[tool.uv]
constraint-dependencies = [
"grpcio<1.65",
]Use constraints to pin a transitive dependency below a known-broken release. Use overrides to widen past an incorrect ceiling. The two solve opposite problems.
Handle requires-python mismatches
uv resolves across all Python versions that requires-python allows. A project declaring requires-python = ">=3.9" forces the resolver to find versions compatible with 3.9 through the latest release, so if a dependency dropped 3.9 support, the resolver fails even on a 3.13 machine.
Either raise the floor in requires-python or pin the dependency to a version that still supports 3.9. For the full walkthrough, see how to fix Python version incompatibility errors in uv.
Learn More
- Resolution in the uv docs covers resolution strategies, overrides, constraints, and
--exclude-newer. - How to use
--exclude-newerfor reproducible Python environments freezes resolution at a point in time, useful for reproducing a failure. - How to use a uv lockfile for reproducible Python environments covers the full lock/sync/upgrade workflow.
- What are uv overrides and constraints? explains when to use overrides vs. constraints vs. sources.
- uv: A Complete Guide walks through the full project workflow.