rust-lang/cargo · error

patch for ` ` in ` ` resolved to more than one…

Error message

patch for `{}` in `{}` resolved to more than one candidate
note: found versions: {}
help: check `{}` patch definition for `{}` in `{}`
help: select only one package using `version = "={}"`

What it means

A `[patch]` entry matched more than one candidate version in the target source. Cargo requires a patch to resolve to exactly one package; multiple matches are ambiguous. The registry collects all matched summaries, and if `summaries.len() > 1` it sorts them and bails, suggesting the user pin with `version = "=<highest>"`.

Solutions

  1. Add a `version` requirement to the patch to select one candidate, e.g. `version = "=1.1.0"`.
  2. Point the patch at a git rev/tag that contains only the desired version.
  3. Use a path patch to a checkout holding a single version.

Example fix

# before
[patch.crates-io]
foo = { git = "https://example.com/foo-fork" }

# after
[patch.crates-io]
foo = { git = "https://example.com/foo-fork", version = "=1.1.0" }
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check: a patch should select exactly one candidate version
fn patch_has_single_candidate(available: &[semver::Version], req: &semver::VersionReq) -> bool {
    available.iter().filter(|v| req.matches(v)).count() == 1
}

Prevention

When it happens

Trigger: `[patch.crates-io] foo = { git = "https://..." }` where the git repo publishes multiple versions of `foo` (e.g. 1.0.0 and 1.1.0) with no version constraint to disambiguate.

Common situations: Patching to a fork or monorepo that contains several versions of the crate. Forgetting the `version` key in a patch entry. Upstream repo gaining new versions after the patch was written.

Related errors


AI-assisted analysis of rust-lang/cargo@98a09e7e7d (2026-08-11). Data as JSON: /api/errors/24a764135e223c78. Report an issue: GitHub.

Appendix: source

Thrown at src/workspace/registry.rs:980

    locked: &Option<LockedPatchDependency>,
    mut summaries: Vec<Summary>,
    source: &dyn Source,
) -> CargoResult<(Summary, Option<PackageId>)> {
    if summaries.len() == 1 {
        return Ok((summaries.pop().unwrap(), None));
    }
    if summaries.len() > 1 {
        // TODO: In the future, it might be nice to add all of these
        // candidates so that version selection would just pick the
        // appropriate one. However, as this is currently structured, if we
        // added these all as patches, the unselected versions would end up in
        // the "unused patch" listing, and trigger a warning. It might take a
        // fair bit of restructuring to make that work cleanly, and there
        // isn't any demand at this time to support that.
        let mut vers: Vec<_> = summaries.iter().map(|summary| summary.version()).collect();
        vers.sort();
        let versions: Vec<_> = vers.into_iter().map(|v| v.to_string()).collect();
        return Err(anyhow::anyhow!(
            "patch for `{}` in `{}` resolved to more than one candidate\n\
            note: found versions: {}\n\
            help: check `{}` patch definition for `{}` in `{}`\n\
            help: select only one package using `version = \"={}\"`",
            &original_patch.dep.package_name(),
            &original_patch.dep.source_id(),
            versions.join(", "),
            &original_patch.dep.package_name(),
            orig_patch_url,
            original_patch.loc,
            versions.last().unwrap()
        ));
    }
    assert!(summaries.is_empty());
    // No summaries found, try to help the user figure out what is wrong.
    if let Some(locked) = locked {
        // Since the locked patch did not match anything, try the unlocked one.
        let orig_matches = source

View on GitHub (pinned to 98a09e7e7d)