Cargo Lock Conflict — Failed to Select a Version for the Requirement
Resolve Cargo version selection and lockfile conflicts by inspecting the dependency tree, aligning semver requirements, and regenerating Cargo.lock in the right place.
Symptoms
- error: failed to select a version for the requirement `foo = ^2`
- the lock file needs to be updated but --locked was passed to cargo
- candidate versions found which didn't match: 1.8.0, 1.7.2
- error: multiple packages link to native library openssl
Possible causes
- Two dependencies require semver ranges that do not overlap
- Cargo.lock is stale after editing Cargo.toml and the build runs with --locked
- The selected version was yanked, or an offline registry cache lacks it
- Two crates bind the same native library through the links key
- The pinned toolchain is older than a dependency's MSRV or edition requirement
Diagnosis steps
Inspect which crates constrain the conflicting package
cargo tree -i some-crateInterpretation
The inverted tree lists every dependent and the version requirement it places. Conflicting requirements from two branches explain the resolver failure.
List duplicate versions in the graph
cargo tree -dInterpretation
Duplicates are allowed for most crates but impossible for crates that use links, and they often reveal an outdated direct dependency.
Reproduce the lockfile failure explicitly
cargo build --lockedInterpretation
The error names the package whose locked version no longer satisfies Cargo.toml. That confirms a stale lock rather than a registry problem.
Check the toolchain against the requirements
rustc --version; cargo --version; cargo metadata --format-version 1 --no-deps > /dev/nullInterpretation
Metadata parsing errors or rust-version conflicts show the manifest needs a newer compiler than the pinned toolchain provides.
Root cause
Cargo could not produce a dependency graph that satisfies every version requirement at once. That happens when two crates demand incompatible semver ranges, when a lockfile no longer matches an edited manifest, when a required version is unavailable in the registry or cache, or when two crates try to link the same native library.
Fix
Align the direct dependencies so their requirements overlap, update both sides with cargo update -p crate --precise version, regenerate the lockfile with cargo update after manifest edits, and commit Cargo.lock for binaries. For links conflicts, replace or upgrade one of the native bindings. In CI and Docker, generate the lockfile in the same build stage that compiles, and avoid --locked until the lockfile is current.
Risk note
cargo update can pull semver-compatible but behavior-changing transitive releases, so review the diff and run tests. Deleting Cargo.lock discards reproducible versions and may break a pinned MSRV. Relaxing a version requirement to silence the resolver can introduce an untested major upgrade.
Verify the fix
Run cargo build --locked and cargo test --locked from a clean checkout and in the CI image, confirm cargo tree -d shows only intentional duplicates, and verify the native links conflict is gone. The same lockfile must build on the pinned toolchain without network surprises.
The resolver is telling you the graph is impossible
Cargo resolves the whole dependency graph at once. A failure such as:
error: failed to select a version for the requirement `rand = "^0.9"`
candidate versions found which didn't match: 0.8.5, 0.8.4
location searched: crates.io index
required by package `telemetry v0.4.1`
means one dependent wants rand 0.9 while another part of the graph can only supply 0.8.x. Cargo will not silently pick a version outside a requirement, so the build stops.
Read the inverted tree first
cargo tree -i rand
cargo tree -d
cargo build --locked
cargo tree -i shows every dependent and its requirement. cargo tree -d exposes duplicate versions; most are harmless, but crates using the links key cannot coexist because only one package may bind a native library. cargo build --locked isolates a stale lockfile: the error names the package whose locked version no longer matches the manifest.
Align, update, then lock
cargo update -p rand --precise 0.9.1
cargo update
cargo build
Update both direct dependents rather than forcing one backwards. When a transitive crate is pinned by an old top-level dependency, upgrading that dependency is the real fix. For binaries, commit Cargo.lock so CI and Docker builds resolve the same graph. Run cargo build --locked only after the lockfile has been regenerated, not as a way to force an old lockfile to fit a new manifest.
For links conflicts, the error names both packages. Replace one native binding, or upgrade until both depend on the same -sys crate version.
Verify reproducibility
Build from a clean checkout with cargo build --locked and run the test suite in the same container image used for release. cargo tree -d should show only duplicates you have accepted, and the toolchain pinned in rust-toolchain.toml must still satisfy every rust-version in the graph.