← Debugging Guides

阅读中文版本

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.

intermediatedebuggingrustcargodependencies

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-crate

Interpretation

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 -d

Interpretation

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 --locked

Interpretation

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/null

Interpretation

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.