# 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](https://pydevtools.com/handbook/how-to/how-to-install-uv.md)
- A uv project with a `pyproject.toml`

## Read the error message

[uv](https://pydevtools.com/handbook/reference/uv.md) prints a derivation chain that traces the conflict from your project's requirements to the incompatible ranges:

```console
$ 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.

```console
$ 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 {{< term "lockfile" >}}:

```console
$ 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:

```console
$ 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 {{< term "transitive-dependency" "transitive dependency" >}} declares an incorrect upper bound, override it. Add `override-dependencies` under `[tool.uv]` in [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md) to replace that package's declared range:

```toml {filename="pyproject.toml"}
[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:

```toml {filename="pyproject.toml"}
[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](https://pydevtools.com/handbook/how-to/how-to-fix-python-version-incompatibility-errors-in-uv.md).

## Learn More

- [Resolution](https://docs.astral.sh/uv/concepts/resolution/) in the uv docs covers resolution strategies, overrides, constraints, and `--exclude-newer`.
- [How to use `--exclude-newer` for reproducible Python environments](https://pydevtools.com/handbook/how-to/how-to-use-exclude-newer-for-reproducible-python-environments.md) freezes resolution at a point in time, useful for reproducing a failure.
- [How to use a uv lockfile for reproducible Python environments](https://pydevtools.com/handbook/how-to/how-to-use-a-uv-lockfile-for-reproducible-python-environments.md) covers the full lock/sync/upgrade workflow.
- [What are uv overrides and constraints?](/handbook/explanation/what-are-uv-overrides-and-constraints/) explains when to use overrides vs. constraints vs. sources.
- [uv: A Complete Guide](https://pydevtools.com/handbook/explanation/uv-complete-guide.md) walks through the full project workflow.
